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 15 MetricsLogManager::SerializedLog::SerializedLog() {} 16 MetricsLogManager::SerializedLog::~SerializedLog() {} 17 18 bool MetricsLogManager::SerializedLog::IsEmpty() const { 19 return log_text_.empty(); 20 } 21 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 30 void MetricsLogManager::SerializedLog::Clear() { 31 log_text_.clear(); 32 log_hash_.clear(); 33 } 34 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 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 50 MetricsLogManager::~MetricsLogManager() {} 51 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 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 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 98 bool MetricsLogManager::has_staged_log() const { 99 return !staged_log_.IsEmpty(); 100 } 101 102 void MetricsLogManager::DiscardStagedLog() { 103 staged_log_.Clear(); 104 staged_log_type_ = MetricsLogBase::NO_LOG; 105 } 106 107 void MetricsLogManager::DiscardCurrentLog() { 108 current_log_->CloseLog(); 109 current_log_.reset(); 110 current_log_type_ = MetricsLogBase::NO_LOG; 111 } 112 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 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 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 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 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 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 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 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