1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/gcm_driver/gcm_stats_recorder_impl.h"
10 #include "base/format_macros.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
18 const uint32 MAX_LOGGED_ACTIVITY_COUNT
= 100;
19 const int64 RECEIVED_DATA_MESSAGE_BURST_LENGTH_SECONDS
= 2;
23 // Insert an item to the front of deque while maintaining the size of the deque.
24 // Overflow item is discarded.
26 T
* InsertCircularBuffer(std::deque
<T
>* q
, const T
& item
) {
29 if (q
->size() > MAX_LOGGED_ACTIVITY_COUNT
) {
35 // Helper for getting string representation of the MessageSendStatus enum.
36 std::string
GetMessageSendStatusString(
37 gcm::MCSClient::MessageSendStatus status
) {
39 case gcm::MCSClient::QUEUED
:
41 case gcm::MCSClient::SENT
:
43 case gcm::MCSClient::QUEUE_SIZE_LIMIT_REACHED
:
44 return "QUEUE_SIZE_LIMIT_REACHED";
45 case gcm::MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED
:
46 return "APP_QUEUE_SIZE_LIMIT_REACHED";
47 case gcm::MCSClient::MESSAGE_TOO_LARGE
:
48 return "MESSAGE_TOO_LARGE";
49 case gcm::MCSClient::NO_CONNECTION_ON_ZERO_TTL
:
50 return "NO_CONNECTION_ON_ZERO_TTL";
51 case gcm::MCSClient::TTL_EXCEEDED
:
52 return "TTL_EXCEEDED";
59 // Helper for getting string representation of the
60 // ConnectionFactory::ConnectionResetReason enum.
61 std::string
GetConnectionResetReasonString(
62 gcm::ConnectionFactory::ConnectionResetReason reason
) {
64 case gcm::ConnectionFactory::LOGIN_FAILURE
:
65 return "LOGIN_FAILURE";
66 case gcm::ConnectionFactory::CLOSE_COMMAND
:
67 return "CLOSE_COMMAND";
68 case gcm::ConnectionFactory::HEARTBEAT_FAILURE
:
69 return "HEARTBEAT_FAILURE";
70 case gcm::ConnectionFactory::SOCKET_FAILURE
:
71 return "SOCKET_FAILURE";
72 case gcm::ConnectionFactory::NETWORK_CHANGE
:
73 return "NETWORK_CHANGE";
76 return "UNKNOWN_REASON";
80 // Helper for getting string representation of the RegistrationRequest::Status
82 std::string
GetRegistrationStatusString(
83 gcm::RegistrationRequest::Status status
) {
85 case gcm::RegistrationRequest::SUCCESS
:
87 case gcm::RegistrationRequest::INVALID_PARAMETERS
:
88 return "INVALID_PARAMETERS";
89 case gcm::RegistrationRequest::INVALID_SENDER
:
90 return "INVALID_SENDER";
91 case gcm::RegistrationRequest::AUTHENTICATION_FAILED
:
92 return "AUTHENTICATION_FAILED";
93 case gcm::RegistrationRequest::DEVICE_REGISTRATION_ERROR
:
94 return "DEVICE_REGISTRATION_ERROR";
95 case gcm::RegistrationRequest::UNKNOWN_ERROR
:
96 return "UNKNOWN_ERROR";
97 case gcm::RegistrationRequest::URL_FETCHING_FAILED
:
98 return "URL_FETCHING_FAILED";
99 case gcm::RegistrationRequest::HTTP_NOT_OK
:
100 return "HTTP_NOT_OK";
101 case gcm::RegistrationRequest::RESPONSE_PARSING_FAILED
:
102 return "RESPONSE_PARSING_FAILED";
103 case gcm::RegistrationRequest::REACHED_MAX_RETRIES
:
104 return "REACHED_MAX_RETRIES";
107 return "UNKNOWN_STATUS";
111 // Helper for getting string representation of the RegistrationRequest::Status
113 std::string
GetUnregistrationStatusString(
114 gcm::UnregistrationRequest::Status status
) {
116 case gcm::UnregistrationRequest::SUCCESS
:
118 case gcm::UnregistrationRequest::URL_FETCHING_FAILED
:
119 return "URL_FETCHING_FAILED";
120 case gcm::UnregistrationRequest::NO_RESPONSE_BODY
:
121 return "NO_RESPONSE_BODY";
122 case gcm::UnregistrationRequest::RESPONSE_PARSING_FAILED
:
123 return "RESPONSE_PARSING_FAILED";
124 case gcm::UnregistrationRequest::INCORRECT_APP_ID
:
125 return "INCORRECT_APP_ID";
126 case gcm::UnregistrationRequest::INVALID_PARAMETERS
:
127 return "INVALID_PARAMETERS";
128 case gcm::UnregistrationRequest::SERVICE_UNAVAILABLE
:
129 return "SERVICE_UNAVAILABLE";
130 case gcm::UnregistrationRequest::INTERNAL_SERVER_ERROR
:
131 return "INTERNAL_SERVER_ERROR";
132 case gcm::UnregistrationRequest::HTTP_NOT_OK
:
133 return "HTTP_NOT_OK";
134 case gcm::UnregistrationRequest::UNKNOWN_ERROR
:
135 return "UNKNOWN_ERROR";
138 return "UNKNOWN_STATUS";
144 GCMStatsRecorderImpl::GCMStatsRecorderImpl()
145 : is_recording_(false),
147 data_message_received_since_connected_(false),
148 received_data_message_burst_size_(0) {
151 GCMStatsRecorderImpl::~GCMStatsRecorderImpl() {
154 void GCMStatsRecorderImpl::SetRecording(bool recording
) {
155 is_recording_
= recording
;
158 void GCMStatsRecorderImpl::SetDelegate(Delegate
* delegate
) {
159 delegate_
= delegate
;
162 void GCMStatsRecorderImpl::Clear() {
163 checkin_activities_
.clear();
164 connection_activities_
.clear();
165 registration_activities_
.clear();
166 receiving_activities_
.clear();
167 sending_activities_
.clear();
170 void GCMStatsRecorderImpl::NotifyActivityRecorded() {
172 delegate_
->OnActivityRecorded();
175 void GCMStatsRecorderImpl::RecordCheckin(
176 const std::string
& event
,
177 const std::string
& details
) {
178 CheckinActivity data
;
179 CheckinActivity
* inserted_data
= InsertCircularBuffer(
180 &checkin_activities_
, data
);
181 inserted_data
->event
= event
;
182 inserted_data
->details
= details
;
183 NotifyActivityRecorded();
186 void GCMStatsRecorderImpl::RecordCheckinInitiated(uint64 android_id
) {
189 RecordCheckin("Checkin initiated",
190 base::StringPrintf("Android Id: %" PRIu64
, android_id
));
193 void GCMStatsRecorderImpl::RecordCheckinDelayedDueToBackoff(int64 delay_msec
) {
196 RecordCheckin("Checkin backoff",
197 base::StringPrintf("Delayed for %" PRId64
" msec",
201 void GCMStatsRecorderImpl::RecordCheckinSuccess() {
204 RecordCheckin("Checkin succeeded", std::string());
207 void GCMStatsRecorderImpl::RecordCheckinFailure(std::string status
,
211 RecordCheckin("Checkin failed", base::StringPrintf(
214 will_retry
? " Will retry." : "Will not retry."));
217 void GCMStatsRecorderImpl::RecordConnection(
218 const std::string
& event
,
219 const std::string
& details
) {
220 ConnectionActivity data
;
221 ConnectionActivity
* inserted_data
= InsertCircularBuffer(
222 &connection_activities_
, data
);
223 inserted_data
->event
= event
;
224 inserted_data
->details
= details
;
225 NotifyActivityRecorded();
228 void GCMStatsRecorderImpl::RecordConnectionInitiated(const std::string
& host
) {
229 last_connection_initiation_time_
= base::TimeTicks::Now();
230 last_connection_success_time_
= base::TimeTicks();
231 data_message_received_since_connected_
= false;
234 RecordConnection("Connection initiated", host
);
237 void GCMStatsRecorderImpl::RecordConnectionDelayedDueToBackoff(
241 RecordConnection("Connection backoff",
242 base::StringPrintf("Delayed for %" PRId64
" msec",
246 void GCMStatsRecorderImpl::RecordConnectionSuccess() {
247 DCHECK(!last_connection_initiation_time_
.is_null());
248 UMA_HISTOGRAM_MEDIUM_TIMES(
249 "GCM.ConnectionLatency",
250 (base::TimeTicks::Now() - last_connection_initiation_time_
));
251 last_connection_success_time_
= base::TimeTicks::Now();
252 last_connection_initiation_time_
= base::TimeTicks();
255 RecordConnection("Connection succeeded", std::string());
258 void GCMStatsRecorderImpl::RecordConnectionFailure(int network_error
) {
261 RecordConnection("Connection failed",
262 base::StringPrintf("With network error %d", network_error
));
265 void GCMStatsRecorderImpl::RecordConnectionResetSignaled(
266 ConnectionFactory::ConnectionResetReason reason
) {
269 RecordConnection("Connection reset",
270 GetConnectionResetReasonString(reason
));
273 void GCMStatsRecorderImpl::RecordRegistration(
274 const std::string
& app_id
,
275 const std::string
& senders
,
276 const std::string
& event
,
277 const std::string
& details
) {
278 RegistrationActivity data
;
279 RegistrationActivity
* inserted_data
= InsertCircularBuffer(
280 ®istration_activities_
, data
);
281 inserted_data
->app_id
= app_id
;
282 inserted_data
->sender_ids
= senders
;
283 inserted_data
->event
= event
;
284 inserted_data
->details
= details
;
285 NotifyActivityRecorded();
288 void GCMStatsRecorderImpl::RecordRegistrationSent(
289 const std::string
& app_id
,
290 const std::string
& sender_ids
) {
291 UMA_HISTOGRAM_COUNTS("GCM.RegistrationRequest", 1);
294 RecordRegistration(app_id
, sender_ids
,
295 "Registration request sent", std::string());
298 void GCMStatsRecorderImpl::RecordRegistrationResponse(
299 const std::string
& app_id
,
300 const std::string
& senders
,
301 RegistrationRequest::Status status
) {
304 RecordRegistration(app_id
, senders
,
305 "Registration response received",
306 GetRegistrationStatusString(status
));
309 void GCMStatsRecorderImpl::RecordRegistrationRetryRequested(
310 const std::string
& app_id
,
311 const std::string
& senders
,
315 RecordRegistration(app_id
, senders
,
316 "Registration retry requested",
317 base::StringPrintf("Retries left: %d", retries_left
));
320 void GCMStatsRecorderImpl::RecordUnregistrationSent(
321 const std::string
& app_id
) {
322 UMA_HISTOGRAM_COUNTS("GCM.UnregistrationRequest", 1);
325 RecordRegistration(app_id
, std::string(), "Unregistration request sent",
329 void GCMStatsRecorderImpl::RecordUnregistrationResponse(
330 const std::string
& app_id
,
331 UnregistrationRequest::Status status
) {
334 RecordRegistration(app_id
,
336 "Unregistration response received",
337 GetUnregistrationStatusString(status
));
340 void GCMStatsRecorderImpl::RecordUnregistrationRetryDelayed(
341 const std::string
& app_id
,
345 RecordRegistration(app_id
,
347 "Unregistration retry delayed",
348 base::StringPrintf("Delayed for %" PRId64
" msec",
352 void GCMStatsRecorderImpl::RecordReceiving(
353 const std::string
& app_id
,
354 const std::string
& from
,
355 int message_byte_size
,
356 const std::string
& event
,
357 const std::string
& details
) {
358 ReceivingActivity data
;
359 ReceivingActivity
* inserted_data
= InsertCircularBuffer(
360 &receiving_activities_
, data
);
361 inserted_data
->app_id
= app_id
;
362 inserted_data
->from
= from
;
363 inserted_data
->message_byte_size
= message_byte_size
;
364 inserted_data
->event
= event
;
365 inserted_data
->details
= details
;
366 NotifyActivityRecorded();
369 void GCMStatsRecorderImpl::RecordDataMessageReceived(
370 const std::string
& app_id
,
371 const std::string
& from
,
372 int message_byte_size
,
373 bool to_registered_app
,
374 ReceivedMessageType message_type
) {
375 if (to_registered_app
)
376 UMA_HISTOGRAM_COUNTS("GCM.DataMessageReceived", 1);
378 base::TimeTicks new_timestamp
= base::TimeTicks::Now();
379 if (last_received_data_message_burst_start_time_
.is_null()) {
380 last_received_data_message_burst_start_time_
= new_timestamp
;
381 last_received_data_message_time_within_burst_
= new_timestamp
;
382 received_data_message_burst_size_
= 1;
383 } else if ((new_timestamp
- last_received_data_message_burst_start_time_
) >=
384 base::TimeDelta::FromSeconds(
385 RECEIVED_DATA_MESSAGE_BURST_LENGTH_SECONDS
)) {
386 UMA_HISTOGRAM_LONG_TIMES(
387 "GCM.DataMessageBurstReceivedInterval",
388 (new_timestamp
- last_received_data_message_burst_start_time_
));
389 UMA_HISTOGRAM_COUNTS("GCM.ReceivedDataMessageBurstSize",
390 received_data_message_burst_size_
);
391 last_received_data_message_burst_start_time_
= new_timestamp
;
392 last_received_data_message_time_within_burst_
= new_timestamp
;
393 received_data_message_burst_size_
= 1;
396 "GCM.ReceivedDataMessageIntervalWithinBurst",
397 (new_timestamp
- last_received_data_message_time_within_burst_
));
398 last_received_data_message_time_within_burst_
= new_timestamp
;
399 ++received_data_message_burst_size_
;
401 if (!data_message_received_since_connected_
) {
402 DCHECK(!last_connection_success_time_
.is_null());
403 UMA_HISTOGRAM_TIMES("GCM.FirstReceivedDataMessageLatencyAfterConnection",
404 (new_timestamp
- last_connection_success_time_
));
405 data_message_received_since_connected_
= true;
410 if (!to_registered_app
) {
411 RecordReceiving(app_id
,
415 "No such registered app found");
417 switch(message_type
) {
418 case GCMStatsRecorderImpl::DATA_MESSAGE
:
419 RecordReceiving(app_id
, from
, message_byte_size
, "Data msg received",
422 case GCMStatsRecorderImpl::DELETED_MESSAGES
:
423 RecordReceiving(app_id
, from
, message_byte_size
, "Data msg received",
424 "Message has been deleted on server");
432 void GCMStatsRecorderImpl::CollectActivities(
433 RecordedActivities
* recorder_activities
) const {
434 recorder_activities
->checkin_activities
.insert(
435 recorder_activities
->checkin_activities
.begin(),
436 checkin_activities_
.begin(),
437 checkin_activities_
.end());
438 recorder_activities
->connection_activities
.insert(
439 recorder_activities
->connection_activities
.begin(),
440 connection_activities_
.begin(),
441 connection_activities_
.end());
442 recorder_activities
->registration_activities
.insert(
443 recorder_activities
->registration_activities
.begin(),
444 registration_activities_
.begin(),
445 registration_activities_
.end());
446 recorder_activities
->receiving_activities
.insert(
447 recorder_activities
->receiving_activities
.begin(),
448 receiving_activities_
.begin(),
449 receiving_activities_
.end());
450 recorder_activities
->sending_activities
.insert(
451 recorder_activities
->sending_activities
.begin(),
452 sending_activities_
.begin(),
453 sending_activities_
.end());
456 void GCMStatsRecorderImpl::RecordSending(const std::string
& app_id
,
457 const std::string
& receiver_id
,
458 const std::string
& message_id
,
459 const std::string
& event
,
460 const std::string
& details
) {
461 SendingActivity data
;
462 SendingActivity
* inserted_data
= InsertCircularBuffer(
463 &sending_activities_
, data
);
464 inserted_data
->app_id
= app_id
;
465 inserted_data
->receiver_id
= receiver_id
;
466 inserted_data
->message_id
= message_id
;
467 inserted_data
->event
= event
;
468 inserted_data
->details
= details
;
469 NotifyActivityRecorded();
472 void GCMStatsRecorderImpl::RecordDataSentToWire(
473 const std::string
& app_id
,
474 const std::string
& receiver_id
,
475 const std::string
& message_id
,
479 RecordSending(app_id
, receiver_id
, message_id
, "Data msg sent to wire",
480 base::StringPrintf("Msg queued for %d seconds", queued
));
483 void GCMStatsRecorderImpl::RecordNotifySendStatus(
484 const std::string
& app_id
,
485 const std::string
& receiver_id
,
486 const std::string
& message_id
,
487 gcm::MCSClient::MessageSendStatus status
,
490 UMA_HISTOGRAM_ENUMERATION("GCM.SendMessageStatus", status
,
491 gcm::MCSClient::SEND_STATUS_COUNT
);
498 base::StringPrintf("SEND status: %s",
499 GetMessageSendStatusString(status
).c_str()),
500 base::StringPrintf("Msg size: %d bytes, TTL: %d", byte_size
, ttl
));
503 void GCMStatsRecorderImpl::RecordIncomingSendError(
504 const std::string
& app_id
,
505 const std::string
& receiver_id
,
506 const std::string
& message_id
) {
507 UMA_HISTOGRAM_COUNTS("GCM.IncomingSendErrors", 1);
510 RecordSending(app_id
, receiver_id
, message_id
, "Received 'send error' msg",