• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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       &registration_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