Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / metrics / metrics_log_serializer.cc
1 // Copyright (c) 2012 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 "chrome/browser/metrics/metrics_log_serializer.h"
6
7 #include <string>
8
9 #include "base/base64.h"
10 #include "base/md5.h"
11 #include "base/metrics/histogram.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/prefs/scoped_user_pref_update.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/common/pref_names.h"
16
17 using metrics::MetricsLogBase;
18 using metrics::MetricsLogManager;
19
20 namespace {
21
22 // The number of "initial" logs to save, and hope to send during a future Chrome
23 // session.  Initial logs contain crash stats, and are pretty small.
24 const size_t kInitialLogsPersistLimit = 20;
25
26 // The number of ongoing logs to save persistently, and hope to
27 // send during a this or future sessions.  Note that each log may be pretty
28 // large, as presumably the related "initial" log wasn't sent (probably nothing
29 // was, as the user was probably off-line).  As a result, the log probably kept
30 // accumulating while the "initial" log was stalled, and couldn't be sent.  As a
31 // result, we don't want to save too many of these mega-logs.
32 // A "standard shutdown" will create a small log, including just the data that
33 // was not yet been transmitted, and that is normal (to have exactly one
34 // ongoing_log_ at startup).
35 const size_t kOngoingLogsPersistLimit = 8;
36
37 // The number of bytes each of initial and ongoing logs that must be stored.
38 // This ensures that a reasonable amount of history will be stored even if there
39 // is a long series of very small logs.
40 const size_t kStorageByteLimitPerLogType = 300000;
41
42 // We append (2) more elements to persisted lists: the size of the list and a
43 // checksum of the elements.
44 const size_t kChecksumEntryCount = 2;
45
46 MetricsLogSerializer::LogReadStatus MakeRecallStatusHistogram(
47     MetricsLogSerializer::LogReadStatus status) {
48   UMA_HISTOGRAM_ENUMERATION("PrefService.PersistentLogRecallProtobufs",
49                             status, MetricsLogSerializer::END_RECALL_STATUS);
50   return status;
51 }
52
53 }  // namespace
54
55
56 MetricsLogSerializer::MetricsLogSerializer() {}
57
58 MetricsLogSerializer::~MetricsLogSerializer() {}
59
60 void MetricsLogSerializer::SerializeLogs(
61     const std::vector<MetricsLogManager::SerializedLog>& logs,
62     MetricsLogManager::LogType log_type) {
63   PrefService* local_state = g_browser_process->local_state();
64   DCHECK(local_state);
65   const char* pref = NULL;
66   size_t store_length_limit = 0;
67   switch (log_type) {
68     case MetricsLogBase::INITIAL_STABILITY_LOG:
69       pref = prefs::kMetricsInitialLogs;
70       store_length_limit = kInitialLogsPersistLimit;
71       break;
72     case MetricsLogBase::ONGOING_LOG:
73       pref = prefs::kMetricsOngoingLogs;
74       store_length_limit = kOngoingLogsPersistLimit;
75       break;
76     case MetricsLogBase::NO_LOG:
77       NOTREACHED();
78       return;
79   };
80
81   ListPrefUpdate update(local_state, pref);
82   WriteLogsToPrefList(logs, store_length_limit, kStorageByteLimitPerLogType,
83                       update.Get());
84 }
85
86 void MetricsLogSerializer::DeserializeLogs(
87     MetricsLogManager::LogType log_type,
88     std::vector<MetricsLogManager::SerializedLog>* logs) {
89   DCHECK(logs);
90   PrefService* local_state = g_browser_process->local_state();
91   DCHECK(local_state);
92
93   const char* pref;
94   if (log_type == MetricsLogBase::INITIAL_STABILITY_LOG)
95     pref = prefs::kMetricsInitialLogs;
96   else
97     pref = prefs::kMetricsOngoingLogs;
98
99   const base::ListValue* unsent_logs = local_state->GetList(pref);
100   ReadLogsFromPrefList(*unsent_logs, logs);
101 }
102
103 // static
104 void MetricsLogSerializer::WriteLogsToPrefList(
105     const std::vector<MetricsLogManager::SerializedLog>& local_list,
106     size_t list_length_limit,
107     size_t byte_limit,
108     base::ListValue* list) {
109   // One of the limit arguments must be non-zero.
110   DCHECK(list_length_limit > 0 || byte_limit > 0);
111
112   list->Clear();
113   if (local_list.size() == 0)
114     return;
115
116   size_t start = 0;
117   // If there are too many logs, keep the most recent logs up to the length
118   // limit, and at least to the minimum number of bytes.
119   if (local_list.size() > list_length_limit) {
120     start = local_list.size();
121     size_t bytes_used = 0;
122     for (std::vector<MetricsLogManager::SerializedLog>::const_reverse_iterator
123          it = local_list.rbegin(); it != local_list.rend(); ++it) {
124       size_t log_size = it->log_text().length();
125       if (bytes_used >= byte_limit &&
126           (local_list.size() - start) >= list_length_limit)
127         break;
128       bytes_used += log_size;
129       --start;
130     }
131   }
132   DCHECK_LT(start, local_list.size());
133   if (start >= local_list.size())
134     return;
135
136   // Store size at the beginning of the list.
137   list->Append(base::Value::CreateIntegerValue(local_list.size() - start));
138
139   base::MD5Context ctx;
140   base::MD5Init(&ctx);
141   std::string encoded_log;
142   for (std::vector<MetricsLogManager::SerializedLog>::const_iterator it =
143            local_list.begin() + start;
144        it != local_list.end(); ++it) {
145     // We encode the compressed log as Value::CreateStringValue() expects to
146     // take a valid UTF8 string.
147     base::Base64Encode(it->log_text(), &encoded_log);
148     base::MD5Update(&ctx, encoded_log);
149     list->Append(base::Value::CreateStringValue(encoded_log));
150   }
151
152   // Append hash to the end of the list.
153   base::MD5Digest digest;
154   base::MD5Final(&digest, &ctx);
155   list->Append(base::Value::CreateStringValue(base::MD5DigestToBase16(digest)));
156   DCHECK(list->GetSize() >= 3);  // Minimum of 3 elements (size, data, hash).
157 }
158
159 // static
160 MetricsLogSerializer::LogReadStatus MetricsLogSerializer::ReadLogsFromPrefList(
161     const base::ListValue& list,
162     std::vector<MetricsLogManager::SerializedLog>* local_list) {
163   if (list.GetSize() == 0)
164     return MakeRecallStatusHistogram(LIST_EMPTY);
165   if (list.GetSize() < 3)
166     return MakeRecallStatusHistogram(LIST_SIZE_TOO_SMALL);
167
168   // The size is stored at the beginning of the list.
169   int size;
170   bool valid = (*list.begin())->GetAsInteger(&size);
171   if (!valid)
172     return MakeRecallStatusHistogram(LIST_SIZE_MISSING);
173   // Account for checksum and size included in the list.
174   if (static_cast<unsigned int>(size) !=
175       list.GetSize() - kChecksumEntryCount) {
176     return MakeRecallStatusHistogram(LIST_SIZE_CORRUPTION);
177   }
178
179   // Allocate strings for all of the logs we are going to read in.
180   // Do this ahead of time so that we can decode the string values directly into
181   // the elements of |local_list|, and thereby avoid making copies of the
182   // serialized logs, which can be fairly large.
183   DCHECK(local_list->empty());
184   local_list->resize(size);
185
186   base::MD5Context ctx;
187   base::MD5Init(&ctx);
188   std::string encoded_log;
189   size_t local_index = 0;
190   for (base::ListValue::const_iterator it = list.begin() + 1;
191        it != list.end() - 1;  // Last element is the checksum.
192        ++it, ++local_index) {
193     bool valid = (*it)->GetAsString(&encoded_log);
194     if (!valid) {
195       local_list->clear();
196       return MakeRecallStatusHistogram(LOG_STRING_CORRUPTION);
197     }
198
199     base::MD5Update(&ctx, encoded_log);
200
201     std::string log_text;
202     if (!base::Base64Decode(encoded_log, &log_text)) {
203       local_list->clear();
204       return MakeRecallStatusHistogram(DECODE_FAIL);
205     }
206
207     DCHECK_LT(local_index, local_list->size());
208     (*local_list)[local_index].SwapLogText(&log_text);
209   }
210
211   // Verify checksum.
212   base::MD5Digest digest;
213   base::MD5Final(&digest, &ctx);
214   std::string recovered_md5;
215   // We store the hash at the end of the list.
216   valid = (*(list.end() - 1))->GetAsString(&recovered_md5);
217   if (!valid) {
218     local_list->clear();
219     return MakeRecallStatusHistogram(CHECKSUM_STRING_CORRUPTION);
220   }
221   if (recovered_md5 != base::MD5DigestToBase16(digest)) {
222     local_list->clear();
223     return MakeRecallStatusHistogram(CHECKSUM_CORRUPTION);
224   }
225   return MakeRecallStatusHistogram(RECALL_SUCCESS);
226 }