gcm_stats_recorder_impl.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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
16namespace gcm {
17
18const uint32 MAX_LOGGED_ACTIVITY_COUNT = 100;
19
20namespace {
21
22// Insert an item to the front of deque while maintaining the size of the deque.
23// Overflow item is discarded.
24template <typename T>
25T* 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.
35std::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.
60std::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.
81std::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.
112std::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
143GCMStatsRecorderImpl::GCMStatsRecorderImpl()
144    : is_recording_(false),
145      delegate_(NULL) {
146}
147
148GCMStatsRecorderImpl::~GCMStatsRecorderImpl() {
149}
150
151void GCMStatsRecorderImpl::SetRecording(bool recording) {
152  is_recording_ = recording;
153}
154
155void GCMStatsRecorderImpl::SetDelegate(Delegate* delegate) {
156  delegate_ = delegate;
157}
158
159void GCMStatsRecorderImpl::Clear() {
160  checkin_activities_.clear();
161  connection_activities_.clear();
162  registration_activities_.clear();
163  receiving_activities_.clear();
164  sending_activities_.clear();
165}
166
167void GCMStatsRecorderImpl::NotifyActivityRecorded() {
168  if (delegate_)
169    delegate_->OnActivityRecorded();
170}
171
172void 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
183void 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
190void 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
198void GCMStatsRecorderImpl::RecordCheckinSuccess() {
199  if (!is_recording_)
200    return;
201  RecordCheckin("Checkin succeeded", std::string());
202}
203
204void 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
214void 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
225void GCMStatsRecorderImpl::RecordConnectionInitiated(const std::string& host) {
226  if (!is_recording_)
227    return;
228  RecordConnection("Connection initiated", host);
229}
230
231void 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
240void GCMStatsRecorderImpl::RecordConnectionSuccess() {
241  if (!is_recording_)
242    return;
243  RecordConnection("Connection succeeded", std::string());
244}
245
246void 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
253void GCMStatsRecorderImpl::RecordConnectionResetSignaled(
254      ConnectionFactory::ConnectionResetReason reason) {
255  if (!is_recording_)
256    return;
257  RecordConnection("Connection reset",
258                   GetConnectionResetReasonString(reason));
259}
260
261void 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
276void 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
286void 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
297void 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
308void 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
317void 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
328void 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
340void 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
357void 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
387void 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
411void 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
427void 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
438void 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
458void 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