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.
4
5 #include "components/gcm_driver/gcm_stats_recorder_impl.h"
6
7 #include <deque>
8 #include <vector>
9
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"
15
16 namespace gcm {
17
18 const uint32 MAX_LOGGED_ACTIVITY_COUNT = 100;
19
20 namespace {
21
22 // Insert an item to the front of deque while maintaining the size of the deque.
23 // Overflow item is discarded.
24 template <typename T>
InsertCircularBuffer(std::deque<T> * q,const T & item)25 T* InsertCircularBuffer(std::deque<T>* q, const T& item) {
26 DCHECK(q);
27 q->push_front(item);
28 if (q->size() > MAX_LOGGED_ACTIVITY_COUNT) {
29 q->pop_back();
30 }
31 return &q->front();
32 }
33
34 // Helper for getting string representation of the MessageSendStatus enum.
GetMessageSendStatusString(gcm::MCSClient::MessageSendStatus status)35 std::string GetMessageSendStatusString(
36 gcm::MCSClient::MessageSendStatus status) {
37 switch (status) {
38 case gcm::MCSClient::QUEUED:
39 return "QUEUED";
40 case gcm::MCSClient::SENT:
41 return "SENT";
42 case gcm::MCSClient::QUEUE_SIZE_LIMIT_REACHED:
43 return "QUEUE_SIZE_LIMIT_REACHED";
44 case gcm::MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED:
45 return "APP_QUEUE_SIZE_LIMIT_REACHED";
46 case gcm::MCSClient::MESSAGE_TOO_LARGE:
47 return "MESSAGE_TOO_LARGE";
48 case gcm::MCSClient::NO_CONNECTION_ON_ZERO_TTL:
49 return "NO_CONNECTION_ON_ZERO_TTL";
50 case gcm::MCSClient::TTL_EXCEEDED:
51 return "TTL_EXCEEDED";
52 default:
53 NOTREACHED();
54 return "UNKNOWN";
55 }
56 }
57
58 // Helper for getting string representation of the
59 // ConnectionFactory::ConnectionResetReason enum.
GetConnectionResetReasonString(gcm::ConnectionFactory::ConnectionResetReason reason)60 std::string GetConnectionResetReasonString(
61 gcm::ConnectionFactory::ConnectionResetReason reason) {
62 switch (reason) {
63 case gcm::ConnectionFactory::LOGIN_FAILURE:
64 return "LOGIN_FAILURE";
65 case gcm::ConnectionFactory::CLOSE_COMMAND:
66 return "CLOSE_COMMAND";
67 case gcm::ConnectionFactory::HEARTBEAT_FAILURE:
68 return "HEARTBEAT_FAILURE";
69 case gcm::ConnectionFactory::SOCKET_FAILURE:
70 return "SOCKET_FAILURE";
71 case gcm::ConnectionFactory::NETWORK_CHANGE:
72 return "NETWORK_CHANGE";
73 default:
74 NOTREACHED();
75 return "UNKNOWN_REASON";
76 }
77 }
78
79 // Helper for getting string representation of the RegistrationRequest::Status
80 // enum.
GetRegistrationStatusString(gcm::RegistrationRequest::Status status)81 std::string GetRegistrationStatusString(
82 gcm::RegistrationRequest::Status status) {
83 switch (status) {
84 case gcm::RegistrationRequest::SUCCESS:
85 return "SUCCESS";
86 case gcm::RegistrationRequest::INVALID_PARAMETERS:
87 return "INVALID_PARAMETERS";
88 case gcm::RegistrationRequest::INVALID_SENDER:
89 return "INVALID_SENDER";
90 case gcm::RegistrationRequest::AUTHENTICATION_FAILED:
91 return "AUTHENTICATION_FAILED";
92 case gcm::RegistrationRequest::DEVICE_REGISTRATION_ERROR:
93 return "DEVICE_REGISTRATION_ERROR";
94 case gcm::RegistrationRequest::UNKNOWN_ERROR:
95 return "UNKNOWN_ERROR";
96 case gcm::RegistrationRequest::URL_FETCHING_FAILED:
97 return "URL_FETCHING_FAILED";
98 case gcm::RegistrationRequest::HTTP_NOT_OK:
99 return "HTTP_NOT_OK";
100 case gcm::RegistrationRequest::RESPONSE_PARSING_FAILED:
101 return "RESPONSE_PARSING_FAILED";
102 case gcm::RegistrationRequest::REACHED_MAX_RETRIES:
103 return "REACHED_MAX_RETRIES";
104 default:
105 NOTREACHED();
106 return "UNKNOWN_STATUS";
107 }
108 }
109
110 // Helper for getting string representation of the RegistrationRequest::Status
111 // enum.
GetUnregistrationStatusString(gcm::UnregistrationRequest::Status status)112 std::string GetUnregistrationStatusString(
113 gcm::UnregistrationRequest::Status status) {
114 switch (status) {
115 case gcm::UnregistrationRequest::SUCCESS:
116 return "SUCCESS";
117 case gcm::UnregistrationRequest::URL_FETCHING_FAILED:
118 return "URL_FETCHING_FAILED";
119 case gcm::UnregistrationRequest::NO_RESPONSE_BODY:
120 return "NO_RESPONSE_BODY";
121 case gcm::UnregistrationRequest::RESPONSE_PARSING_FAILED:
122 return "RESPONSE_PARSING_FAILED";
123 case gcm::UnregistrationRequest::INCORRECT_APP_ID:
124 return "INCORRECT_APP_ID";
125 case gcm::UnregistrationRequest::INVALID_PARAMETERS:
126 return "INVALID_PARAMETERS";
127 case gcm::UnregistrationRequest::SERVICE_UNAVAILABLE:
128 return "SERVICE_UNAVAILABLE";
129 case gcm::UnregistrationRequest::INTERNAL_SERVER_ERROR:
130 return "INTERNAL_SERVER_ERROR";
131 case gcm::UnregistrationRequest::HTTP_NOT_OK:
132 return "HTTP_NOT_OK";
133 case gcm::UnregistrationRequest::UNKNOWN_ERROR:
134 return "UNKNOWN_ERROR";
135 default:
136 NOTREACHED();
137 return "UNKNOWN_STATUS";
138 }
139 }
140
141 } // namespace
142
GCMStatsRecorderImpl()143 GCMStatsRecorderImpl::GCMStatsRecorderImpl()
144 : is_recording_(false),
145 delegate_(NULL) {
146 }
147
~GCMStatsRecorderImpl()148 GCMStatsRecorderImpl::~GCMStatsRecorderImpl() {
149 }
150
SetRecording(bool recording)151 void GCMStatsRecorderImpl::SetRecording(bool recording) {
152 is_recording_ = recording;
153 }
154
SetDelegate(Delegate * delegate)155 void GCMStatsRecorderImpl::SetDelegate(Delegate* delegate) {
156 delegate_ = delegate;
157 }
158
Clear()159 void GCMStatsRecorderImpl::Clear() {
160 checkin_activities_.clear();
161 connection_activities_.clear();
162 registration_activities_.clear();
163 receiving_activities_.clear();
164 sending_activities_.clear();
165 }
166
NotifyActivityRecorded()167 void GCMStatsRecorderImpl::NotifyActivityRecorded() {
168 if (delegate_)
169 delegate_->OnActivityRecorded();
170 }
171
RecordCheckin(const std::string & event,const std::string & details)172 void GCMStatsRecorderImpl::RecordCheckin(
173 const std::string& event,
174 const std::string& details) {
175 CheckinActivity data;
176 CheckinActivity* inserted_data = InsertCircularBuffer(
177 &checkin_activities_, data);
178 inserted_data->event = event;
179 inserted_data->details = details;
180 NotifyActivityRecorded();
181 }
182
RecordCheckinInitiated(uint64 android_id)183 void GCMStatsRecorderImpl::RecordCheckinInitiated(uint64 android_id) {
184 if (!is_recording_)
185 return;
186 RecordCheckin("Checkin initiated",
187 base::StringPrintf("Android Id: %" PRIu64, android_id));
188 }
189
RecordCheckinDelayedDueToBackoff(int64 delay_msec)190 void GCMStatsRecorderImpl::RecordCheckinDelayedDueToBackoff(int64 delay_msec) {
191 if (!is_recording_)
192 return;
193 RecordCheckin("Checkin backoff",
194 base::StringPrintf("Delayed for %" PRId64 " msec",
195 delay_msec));
196 }
197
RecordCheckinSuccess()198 void GCMStatsRecorderImpl::RecordCheckinSuccess() {
199 if (!is_recording_)
200 return;
201 RecordCheckin("Checkin succeeded", std::string());
202 }
203
RecordCheckinFailure(std::string status,bool will_retry)204 void GCMStatsRecorderImpl::RecordCheckinFailure(std::string status,
205 bool will_retry) {
206 if (!is_recording_)
207 return;
208 RecordCheckin("Checkin failed", base::StringPrintf(
209 "%s.%s",
210 status.c_str(),
211 will_retry ? " Will retry." : "Will not retry."));
212 }
213
RecordConnection(const std::string & event,const std::string & details)214 void GCMStatsRecorderImpl::RecordConnection(
215 const std::string& event,
216 const std::string& details) {
217 ConnectionActivity data;
218 ConnectionActivity* inserted_data = InsertCircularBuffer(
219 &connection_activities_, data);
220 inserted_data->event = event;
221 inserted_data->details = details;
222 NotifyActivityRecorded();
223 }
224
RecordConnectionInitiated(const std::string & host)225 void GCMStatsRecorderImpl::RecordConnectionInitiated(const std::string& host) {
226 if (!is_recording_)
227 return;
228 RecordConnection("Connection initiated", host);
229 }
230
RecordConnectionDelayedDueToBackoff(int64 delay_msec)231 void GCMStatsRecorderImpl::RecordConnectionDelayedDueToBackoff(
232 int64 delay_msec) {
233 if (!is_recording_)
234 return;
235 RecordConnection("Connection backoff",
236 base::StringPrintf("Delayed for %" PRId64 " msec",
237 delay_msec));
238 }
239
RecordConnectionSuccess()240 void GCMStatsRecorderImpl::RecordConnectionSuccess() {
241 if (!is_recording_)
242 return;
243 RecordConnection("Connection succeeded", std::string());
244 }
245
RecordConnectionFailure(int network_error)246 void GCMStatsRecorderImpl::RecordConnectionFailure(int network_error) {
247 if (!is_recording_)
248 return;
249 RecordConnection("Connection failed",
250 base::StringPrintf("With network error %d", network_error));
251 }
252
RecordConnectionResetSignaled(ConnectionFactory::ConnectionResetReason reason)253 void GCMStatsRecorderImpl::RecordConnectionResetSignaled(
254 ConnectionFactory::ConnectionResetReason reason) {
255 if (!is_recording_)
256 return;
257 RecordConnection("Connection reset",
258 GetConnectionResetReasonString(reason));
259 }
260
RecordRegistration(const std::string & app_id,const std::string & sender_ids,const std::string & event,const std::string & details)261 void GCMStatsRecorderImpl::RecordRegistration(
262 const std::string& app_id,
263 const std::string& sender_ids,
264 const std::string& event,
265 const std::string& details) {
266 RegistrationActivity data;
267 RegistrationActivity* inserted_data = InsertCircularBuffer(
268 ®istration_activities_, data);
269 inserted_data->app_id = app_id;
270 inserted_data->sender_ids = sender_ids;
271 inserted_data->event = event;
272 inserted_data->details = details;
273 NotifyActivityRecorded();
274 }
275
RecordRegistrationSent(const std::string & app_id,const std::string & sender_ids)276 void GCMStatsRecorderImpl::RecordRegistrationSent(
277 const std::string& app_id,
278 const std::string& sender_ids) {
279 UMA_HISTOGRAM_COUNTS("GCM.RegistrationRequest", 1);
280 if (!is_recording_)
281 return;
282 RecordRegistration(app_id, sender_ids,
283 "Registration request sent", std::string());
284 }
285
RecordRegistrationResponse(const std::string & app_id,const std::vector<std::string> & sender_ids,RegistrationRequest::Status status)286 void GCMStatsRecorderImpl::RecordRegistrationResponse(
287 const std::string& app_id,
288 const std::vector<std::string>& sender_ids,
289 RegistrationRequest::Status status) {
290 if (!is_recording_)
291 return;
292 RecordRegistration(app_id, JoinString(sender_ids, ","),
293 "Registration response received",
294 GetRegistrationStatusString(status));
295 }
296
RecordRegistrationRetryRequested(const std::string & app_id,const std::vector<std::string> & sender_ids,int retries_left)297 void GCMStatsRecorderImpl::RecordRegistrationRetryRequested(
298 const std::string& app_id,
299 const std::vector<std::string>& sender_ids,
300 int retries_left) {
301 if (!is_recording_)
302 return;
303 RecordRegistration(app_id, JoinString(sender_ids, ","),
304 "Registration retry requested",
305 base::StringPrintf("Retries left: %d", retries_left));
306 }
307
RecordUnregistrationSent(const std::string & app_id)308 void GCMStatsRecorderImpl::RecordUnregistrationSent(
309 const std::string& app_id) {
310 UMA_HISTOGRAM_COUNTS("GCM.UnregistrationRequest", 1);
311 if (!is_recording_)
312 return;
313 RecordRegistration(app_id, std::string(), "Unregistration request sent",
314 std::string());
315 }
316
RecordUnregistrationResponse(const std::string & app_id,UnregistrationRequest::Status status)317 void GCMStatsRecorderImpl::RecordUnregistrationResponse(
318 const std::string& app_id,
319 UnregistrationRequest::Status status) {
320 if (!is_recording_)
321 return;
322 RecordRegistration(app_id,
323 std::string(),
324 "Unregistration response received",
325 GetUnregistrationStatusString(status));
326 }
327
RecordUnregistrationRetryDelayed(const std::string & app_id,int64 delay_msec)328 void GCMStatsRecorderImpl::RecordUnregistrationRetryDelayed(
329 const std::string& app_id,
330 int64 delay_msec) {
331 if (!is_recording_)
332 return;
333 RecordRegistration(app_id,
334 std::string(),
335 "Unregistration retry delayed",
336 base::StringPrintf("Delayed for %" PRId64 " msec",
337 delay_msec));
338 }
339
RecordReceiving(const std::string & app_id,const std::string & from,int message_byte_size,const std::string & event,const std::string & details)340 void GCMStatsRecorderImpl::RecordReceiving(
341 const std::string& app_id,
342 const std::string& from,
343 int message_byte_size,
344 const std::string& event,
345 const std::string& details) {
346 ReceivingActivity data;
347 ReceivingActivity* inserted_data = InsertCircularBuffer(
348 &receiving_activities_, data);
349 inserted_data->app_id = app_id;
350 inserted_data->from = from;
351 inserted_data->message_byte_size = message_byte_size;
352 inserted_data->event = event;
353 inserted_data->details = details;
354 NotifyActivityRecorded();
355 }
356
RecordDataMessageReceived(const std::string & app_id,const std::string & from,int message_byte_size,bool to_registered_app,ReceivedMessageType message_type)357 void GCMStatsRecorderImpl::RecordDataMessageReceived(
358 const std::string& app_id,
359 const std::string& from,
360 int message_byte_size,
361 bool to_registered_app,
362 ReceivedMessageType message_type) {
363 if (to_registered_app)
364 UMA_HISTOGRAM_COUNTS("GCM.DataMessageReceived", 1);
365 if (!is_recording_)
366 return;
367 if (!to_registered_app) {
368 RecordReceiving(app_id, from, message_byte_size, "Data msg received",
369 to_registered_app ? std::string() :
370 "No such registered app found");
371 } else {
372 switch(message_type) {
373 case GCMStatsRecorderImpl::DATA_MESSAGE:
374 RecordReceiving(app_id, from, message_byte_size, "Data msg received",
375 std::string());
376 break;
377 case GCMStatsRecorderImpl::DELETED_MESSAGES:
378 RecordReceiving(app_id, from, message_byte_size, "Data msg received",
379 "Message has been deleted on server");
380 break;
381 default:
382 NOTREACHED();
383 }
384 }
385 }
386
CollectActivities(RecordedActivities * recorder_activities) const387 void GCMStatsRecorderImpl::CollectActivities(
388 RecordedActivities* recorder_activities) const {
389 recorder_activities->checkin_activities.insert(
390 recorder_activities->checkin_activities.begin(),
391 checkin_activities_.begin(),
392 checkin_activities_.end());
393 recorder_activities->connection_activities.insert(
394 recorder_activities->connection_activities.begin(),
395 connection_activities_.begin(),
396 connection_activities_.end());
397 recorder_activities->registration_activities.insert(
398 recorder_activities->registration_activities.begin(),
399 registration_activities_.begin(),
400 registration_activities_.end());
401 recorder_activities->receiving_activities.insert(
402 recorder_activities->receiving_activities.begin(),
403 receiving_activities_.begin(),
404 receiving_activities_.end());
405 recorder_activities->sending_activities.insert(
406 recorder_activities->sending_activities.begin(),
407 sending_activities_.begin(),
408 sending_activities_.end());
409 }
410
RecordSending(const std::string & app_id,const std::string & receiver_id,const std::string & message_id,const std::string & event,const std::string & details)411 void GCMStatsRecorderImpl::RecordSending(const std::string& app_id,
412 const std::string& receiver_id,
413 const std::string& message_id,
414 const std::string& event,
415 const std::string& details) {
416 SendingActivity data;
417 SendingActivity* inserted_data = InsertCircularBuffer(
418 &sending_activities_, data);
419 inserted_data->app_id = app_id;
420 inserted_data->receiver_id = receiver_id;
421 inserted_data->message_id = message_id;
422 inserted_data->event = event;
423 inserted_data->details = details;
424 NotifyActivityRecorded();
425 }
426
RecordDataSentToWire(const std::string & app_id,const std::string & receiver_id,const std::string & message_id,int queued)427 void GCMStatsRecorderImpl::RecordDataSentToWire(
428 const std::string& app_id,
429 const std::string& receiver_id,
430 const std::string& message_id,
431 int queued) {
432 if (!is_recording_)
433 return;
434 RecordSending(app_id, receiver_id, message_id, "Data msg sent to wire",
435 base::StringPrintf("Msg queued for %d seconds", queued));
436 }
437
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)438 void GCMStatsRecorderImpl::RecordNotifySendStatus(
439 const std::string& app_id,
440 const std::string& receiver_id,
441 const std::string& message_id,
442 gcm::MCSClient::MessageSendStatus status,
443 int byte_size,
444 int ttl) {
445 UMA_HISTOGRAM_ENUMERATION("GCM.SendMessageStatus", status,
446 gcm::MCSClient::SEND_STATUS_COUNT);
447 if (!is_recording_)
448 return;
449 RecordSending(
450 app_id,
451 receiver_id,
452 message_id,
453 base::StringPrintf("SEND status: %s",
454 GetMessageSendStatusString(status).c_str()),
455 base::StringPrintf("Msg size: %d bytes, TTL: %d", byte_size, ttl));
456 }
457
RecordIncomingSendError(const std::string & app_id,const std::string & receiver_id,const std::string & message_id)458 void GCMStatsRecorderImpl::RecordIncomingSendError(
459 const std::string& app_id,
460 const std::string& receiver_id,
461 const std::string& message_id) {
462 UMA_HISTOGRAM_COUNTS("GCM.IncomingSendErrors", 1);
463 if (!is_recording_)
464 return;
465 RecordSending(app_id, receiver_id, message_id, "Received 'send error' msg",
466 std::string());
467 }
468
469 } // namespace gcm
470