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/common/metrics/metrics_log_manager.h"
6
7 #include <algorithm>
8
9 #include "base/metrics/histogram.h"
10 #include "base/sha1.h"
11 #include "base/strings/string_util.h"
12 #include "base/timer/elapsed_timer.h"
13 #include "chrome/common/metrics/metrics_log_base.h"
14
SerializedLog()15 MetricsLogManager::SerializedLog::SerializedLog() {}
~SerializedLog()16 MetricsLogManager::SerializedLog::~SerializedLog() {}
17
IsEmpty() const18 bool MetricsLogManager::SerializedLog::IsEmpty() const {
19 return log_text_.empty();
20 }
21
SwapLogText(std::string * log_text)22 void MetricsLogManager::SerializedLog::SwapLogText(std::string* log_text) {
23 log_text_.swap(*log_text);
24 if (log_text_.empty())
25 log_hash_.clear();
26 else
27 log_hash_ = base::SHA1HashString(log_text_);
28 }
29
Clear()30 void MetricsLogManager::SerializedLog::Clear() {
31 log_text_.clear();
32 log_hash_.clear();
33 }
34
Swap(MetricsLogManager::SerializedLog * other)35 void MetricsLogManager::SerializedLog::Swap(
36 MetricsLogManager::SerializedLog* other) {
37 log_text_.swap(other->log_text_);
38 log_hash_.swap(other->log_hash_);
39 }
40
MetricsLogManager()41 MetricsLogManager::MetricsLogManager()
42 : unsent_logs_loaded_(false),
43 current_log_type_(MetricsLogBase::NO_LOG),
44 paused_log_type_(MetricsLogBase::NO_LOG),
45 staged_log_type_(MetricsLogBase::NO_LOG),
46 max_ongoing_log_store_size_(0),
47 last_provisional_store_index_(-1),
48 last_provisional_store_type_(MetricsLogBase::INITIAL_LOG) {}
49
~MetricsLogManager()50 MetricsLogManager::~MetricsLogManager() {}
51
BeginLoggingWithLog(MetricsLogBase * log,LogType log_type)52 void MetricsLogManager::BeginLoggingWithLog(MetricsLogBase* log,
53 LogType log_type) {
54 DCHECK_NE(MetricsLogBase::NO_LOG, log_type);
55 DCHECK(!current_log_.get());
56 current_log_.reset(log);
57 current_log_type_ = log_type;
58 }
59
FinishCurrentLog()60 void MetricsLogManager::FinishCurrentLog() {
61 DCHECK(current_log_.get());
62 DCHECK_NE(MetricsLogBase::NO_LOG, current_log_type_);
63 current_log_->CloseLog();
64 SerializedLog compressed_log;
65 CompressCurrentLog(&compressed_log);
66 if (!compressed_log.IsEmpty())
67 StoreLog(&compressed_log, current_log_type_, NORMAL_STORE);
68 current_log_.reset();
69 current_log_type_ = MetricsLogBase::NO_LOG;
70 }
71
StageNextLogForUpload()72 void MetricsLogManager::StageNextLogForUpload() {
73 // Prioritize initial logs for uploading.
74 std::vector<SerializedLog>* source_list =
75 unsent_initial_logs_.empty() ? &unsent_ongoing_logs_
76 : &unsent_initial_logs_;
77 LogType source_type = (source_list == &unsent_ongoing_logs_) ?
78 MetricsLogBase::ONGOING_LOG : MetricsLogBase::INITIAL_LOG;
79 // CHECK, rather than DCHECK, because swap()ing with an empty list causes
80 // hard-to-identify crashes much later.
81 CHECK(!source_list->empty());
82 DCHECK(staged_log_.IsEmpty());
83 DCHECK_EQ(MetricsLogBase::NO_LOG, staged_log_type_);
84 staged_log_.Swap(&source_list->back());
85 staged_log_type_ = source_type;
86 source_list->pop_back();
87
88 // If the staged log was the last provisional store, clear that.
89 if (last_provisional_store_index_ != -1) {
90 if (source_type == last_provisional_store_type_ &&
91 static_cast<unsigned int>(last_provisional_store_index_) ==
92 source_list->size()) {
93 last_provisional_store_index_ = -1;
94 }
95 }
96 }
97
has_staged_log() const98 bool MetricsLogManager::has_staged_log() const {
99 return !staged_log_.IsEmpty();
100 }
101
DiscardStagedLog()102 void MetricsLogManager::DiscardStagedLog() {
103 staged_log_.Clear();
104 staged_log_type_ = MetricsLogBase::NO_LOG;
105 }
106
DiscardCurrentLog()107 void MetricsLogManager::DiscardCurrentLog() {
108 current_log_->CloseLog();
109 current_log_.reset();
110 current_log_type_ = MetricsLogBase::NO_LOG;
111 }
112
PauseCurrentLog()113 void MetricsLogManager::PauseCurrentLog() {
114 DCHECK(!paused_log_.get());
115 DCHECK_EQ(MetricsLogBase::NO_LOG, paused_log_type_);
116 paused_log_.reset(current_log_.release());
117 paused_log_type_ = current_log_type_;
118 current_log_type_ = MetricsLogBase::NO_LOG;
119 }
120
ResumePausedLog()121 void MetricsLogManager::ResumePausedLog() {
122 DCHECK(!current_log_.get());
123 DCHECK_EQ(MetricsLogBase::NO_LOG, current_log_type_);
124 current_log_.reset(paused_log_.release());
125 current_log_type_ = paused_log_type_;
126 paused_log_type_ = MetricsLogBase::NO_LOG;
127 }
128
StoreStagedLogAsUnsent(StoreType store_type)129 void MetricsLogManager::StoreStagedLogAsUnsent(StoreType store_type) {
130 DCHECK(has_staged_log());
131
132 // If compressing the log failed, there's nothing to store.
133 if (staged_log_.IsEmpty())
134 return;
135
136 StoreLog(&staged_log_, staged_log_type_, store_type);
137 DiscardStagedLog();
138 }
139
StoreLog(SerializedLog * log,LogType log_type,StoreType store_type)140 void MetricsLogManager::StoreLog(SerializedLog* log,
141 LogType log_type,
142 StoreType store_type) {
143 DCHECK_NE(MetricsLogBase::NO_LOG, log_type);
144 std::vector<SerializedLog>* destination_list =
145 (log_type == MetricsLogBase::INITIAL_LOG) ? &unsent_initial_logs_
146 : &unsent_ongoing_logs_;
147 destination_list->push_back(SerializedLog());
148 destination_list->back().Swap(log);
149
150 if (store_type == PROVISIONAL_STORE) {
151 last_provisional_store_index_ = destination_list->size() - 1;
152 last_provisional_store_type_ = log_type;
153 }
154 }
155
DiscardLastProvisionalStore()156 void MetricsLogManager::DiscardLastProvisionalStore() {
157 if (last_provisional_store_index_ == -1)
158 return;
159 std::vector<SerializedLog>* source_list =
160 (last_provisional_store_type_ == MetricsLogBase::ONGOING_LOG) ?
161 &unsent_ongoing_logs_ : &unsent_initial_logs_;
162 DCHECK_LT(static_cast<unsigned int>(last_provisional_store_index_),
163 source_list->size());
164 source_list->erase(source_list->begin() + last_provisional_store_index_);
165 last_provisional_store_index_ = -1;
166 }
167
PersistUnsentLogs()168 void MetricsLogManager::PersistUnsentLogs() {
169 DCHECK(log_serializer_.get());
170 if (!log_serializer_.get())
171 return;
172 DCHECK(unsent_logs_loaded_);
173 if (!unsent_logs_loaded_)
174 return;
175
176 base::ElapsedTimer timer;
177 // Remove any ongoing logs that are over the serialization size limit.
178 if (max_ongoing_log_store_size_) {
179 for (std::vector<SerializedLog>::iterator it = unsent_ongoing_logs_.begin();
180 it != unsent_ongoing_logs_.end();) {
181 size_t log_size = it->log_text().length();
182 if (log_size > max_ongoing_log_store_size_) {
183 UMA_HISTOGRAM_COUNTS("UMA.Large Accumulated Log Not Persisted",
184 static_cast<int>(log_size));
185 it = unsent_ongoing_logs_.erase(it);
186 } else {
187 ++it;
188 }
189 }
190 }
191 log_serializer_->SerializeLogs(unsent_initial_logs_,
192 MetricsLogBase::INITIAL_LOG);
193 log_serializer_->SerializeLogs(unsent_ongoing_logs_,
194 MetricsLogBase::ONGOING_LOG);
195 UMA_HISTOGRAM_TIMES("UMA.StoreLogsTime", timer.Elapsed());
196 }
197
LoadPersistedUnsentLogs()198 void MetricsLogManager::LoadPersistedUnsentLogs() {
199 DCHECK(log_serializer_.get());
200 if (!log_serializer_.get())
201 return;
202
203 base::ElapsedTimer timer;
204 log_serializer_->DeserializeLogs(MetricsLogBase::INITIAL_LOG,
205 &unsent_initial_logs_);
206 log_serializer_->DeserializeLogs(MetricsLogBase::ONGOING_LOG,
207 &unsent_ongoing_logs_);
208 UMA_HISTOGRAM_TIMES("UMA.LoadLogsTime", timer.Elapsed());
209
210 unsent_logs_loaded_ = true;
211 }
212
CompressCurrentLog(SerializedLog * compressed_log)213 void MetricsLogManager::CompressCurrentLog(SerializedLog* compressed_log) {
214 std::string log_text;
215 current_log_->GetEncodedLog(&log_text);
216 compressed_log->SwapLogText(&log_text);
217 }
218