// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/common/metrics/metrics_log_manager.h" #include #include "base/metrics/histogram.h" #include "base/sha1.h" #include "base/strings/string_util.h" #include "base/timer/elapsed_timer.h" #include "chrome/common/metrics/metrics_log_base.h" MetricsLogManager::SerializedLog::SerializedLog() {} MetricsLogManager::SerializedLog::~SerializedLog() {} bool MetricsLogManager::SerializedLog::IsEmpty() const { return log_text_.empty(); } void MetricsLogManager::SerializedLog::SwapLogText(std::string* log_text) { log_text_.swap(*log_text); if (log_text_.empty()) log_hash_.clear(); else log_hash_ = base::SHA1HashString(log_text_); } void MetricsLogManager::SerializedLog::Clear() { log_text_.clear(); log_hash_.clear(); } void MetricsLogManager::SerializedLog::Swap( MetricsLogManager::SerializedLog* other) { log_text_.swap(other->log_text_); log_hash_.swap(other->log_hash_); } MetricsLogManager::MetricsLogManager() : unsent_logs_loaded_(false), current_log_type_(MetricsLogBase::NO_LOG), paused_log_type_(MetricsLogBase::NO_LOG), staged_log_type_(MetricsLogBase::NO_LOG), max_ongoing_log_store_size_(0), last_provisional_store_index_(-1), last_provisional_store_type_(MetricsLogBase::INITIAL_LOG) {} MetricsLogManager::~MetricsLogManager() {} void MetricsLogManager::BeginLoggingWithLog(MetricsLogBase* log, LogType log_type) { DCHECK_NE(MetricsLogBase::NO_LOG, log_type); DCHECK(!current_log_.get()); current_log_.reset(log); current_log_type_ = log_type; } void MetricsLogManager::FinishCurrentLog() { DCHECK(current_log_.get()); DCHECK_NE(MetricsLogBase::NO_LOG, current_log_type_); current_log_->CloseLog(); SerializedLog compressed_log; CompressCurrentLog(&compressed_log); if (!compressed_log.IsEmpty()) StoreLog(&compressed_log, current_log_type_, NORMAL_STORE); current_log_.reset(); current_log_type_ = MetricsLogBase::NO_LOG; } void MetricsLogManager::StageNextLogForUpload() { // Prioritize initial logs for uploading. std::vector* source_list = unsent_initial_logs_.empty() ? &unsent_ongoing_logs_ : &unsent_initial_logs_; LogType source_type = (source_list == &unsent_ongoing_logs_) ? MetricsLogBase::ONGOING_LOG : MetricsLogBase::INITIAL_LOG; // CHECK, rather than DCHECK, because swap()ing with an empty list causes // hard-to-identify crashes much later. CHECK(!source_list->empty()); DCHECK(staged_log_.IsEmpty()); DCHECK_EQ(MetricsLogBase::NO_LOG, staged_log_type_); staged_log_.Swap(&source_list->back()); staged_log_type_ = source_type; source_list->pop_back(); // If the staged log was the last provisional store, clear that. if (last_provisional_store_index_ != -1) { if (source_type == last_provisional_store_type_ && static_cast(last_provisional_store_index_) == source_list->size()) { last_provisional_store_index_ = -1; } } } bool MetricsLogManager::has_staged_log() const { return !staged_log_.IsEmpty(); } void MetricsLogManager::DiscardStagedLog() { staged_log_.Clear(); staged_log_type_ = MetricsLogBase::NO_LOG; } void MetricsLogManager::DiscardCurrentLog() { current_log_->CloseLog(); current_log_.reset(); current_log_type_ = MetricsLogBase::NO_LOG; } void MetricsLogManager::PauseCurrentLog() { DCHECK(!paused_log_.get()); DCHECK_EQ(MetricsLogBase::NO_LOG, paused_log_type_); paused_log_.reset(current_log_.release()); paused_log_type_ = current_log_type_; current_log_type_ = MetricsLogBase::NO_LOG; } void MetricsLogManager::ResumePausedLog() { DCHECK(!current_log_.get()); DCHECK_EQ(MetricsLogBase::NO_LOG, current_log_type_); current_log_.reset(paused_log_.release()); current_log_type_ = paused_log_type_; paused_log_type_ = MetricsLogBase::NO_LOG; } void MetricsLogManager::StoreStagedLogAsUnsent(StoreType store_type) { DCHECK(has_staged_log()); // If compressing the log failed, there's nothing to store. if (staged_log_.IsEmpty()) return; StoreLog(&staged_log_, staged_log_type_, store_type); DiscardStagedLog(); } void MetricsLogManager::StoreLog(SerializedLog* log, LogType log_type, StoreType store_type) { DCHECK_NE(MetricsLogBase::NO_LOG, log_type); std::vector* destination_list = (log_type == MetricsLogBase::INITIAL_LOG) ? &unsent_initial_logs_ : &unsent_ongoing_logs_; destination_list->push_back(SerializedLog()); destination_list->back().Swap(log); if (store_type == PROVISIONAL_STORE) { last_provisional_store_index_ = destination_list->size() - 1; last_provisional_store_type_ = log_type; } } void MetricsLogManager::DiscardLastProvisionalStore() { if (last_provisional_store_index_ == -1) return; std::vector* source_list = (last_provisional_store_type_ == MetricsLogBase::ONGOING_LOG) ? &unsent_ongoing_logs_ : &unsent_initial_logs_; DCHECK_LT(static_cast(last_provisional_store_index_), source_list->size()); source_list->erase(source_list->begin() + last_provisional_store_index_); last_provisional_store_index_ = -1; } void MetricsLogManager::PersistUnsentLogs() { DCHECK(log_serializer_.get()); if (!log_serializer_.get()) return; DCHECK(unsent_logs_loaded_); if (!unsent_logs_loaded_) return; base::ElapsedTimer timer; // Remove any ongoing logs that are over the serialization size limit. if (max_ongoing_log_store_size_) { for (std::vector::iterator it = unsent_ongoing_logs_.begin(); it != unsent_ongoing_logs_.end();) { size_t log_size = it->log_text().length(); if (log_size > max_ongoing_log_store_size_) { UMA_HISTOGRAM_COUNTS("UMA.Large Accumulated Log Not Persisted", static_cast(log_size)); it = unsent_ongoing_logs_.erase(it); } else { ++it; } } } log_serializer_->SerializeLogs(unsent_initial_logs_, MetricsLogBase::INITIAL_LOG); log_serializer_->SerializeLogs(unsent_ongoing_logs_, MetricsLogBase::ONGOING_LOG); UMA_HISTOGRAM_TIMES("UMA.StoreLogsTime", timer.Elapsed()); } void MetricsLogManager::LoadPersistedUnsentLogs() { DCHECK(log_serializer_.get()); if (!log_serializer_.get()) return; base::ElapsedTimer timer; log_serializer_->DeserializeLogs(MetricsLogBase::INITIAL_LOG, &unsent_initial_logs_); log_serializer_->DeserializeLogs(MetricsLogBase::ONGOING_LOG, &unsent_ongoing_logs_); UMA_HISTOGRAM_TIMES("UMA.LoadLogsTime", timer.Elapsed()); unsent_logs_loaded_ = true; } void MetricsLogManager::CompressCurrentLog(SerializedLog* compressed_log) { std::string log_text; current_log_->GetEncodedLog(&log_text); compressed_log->SwapLogText(&log_text); }