blob: 017c5fd56011eac84898a05735d850eab4928288 [file] [log] [blame]
// 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 <algorithm>
#include "base/metrics/histogram.h"
#include "base/sha1.h"
#include "base/strings/string_util.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()
: current_log_type_(NO_LOG),
paused_log_type_(NO_LOG),
staged_log_type_(NO_LOG),
max_ongoing_log_store_size_(0),
last_provisional_store_index_(-1),
last_provisional_store_type_(INITIAL_LOG) {}
MetricsLogManager::~MetricsLogManager() {}
void MetricsLogManager::BeginLoggingWithLog(MetricsLogBase* log,
LogType log_type) {
DCHECK(log_type != NO_LOG);
DCHECK(!current_log_.get());
current_log_.reset(log);
current_log_type_ = log_type;
}
void MetricsLogManager::FinishCurrentLog() {
DCHECK(current_log_.get());
DCHECK(current_log_type_ != NO_LOG);
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_ = NO_LOG;
}
void MetricsLogManager::StageNextLogForUpload() {
// Prioritize initial logs for uploading.
std::vector<SerializedLog>* source_list =
unsent_initial_logs_.empty() ? &unsent_ongoing_logs_
: &unsent_initial_logs_;
LogType source_type = (source_list == &unsent_ongoing_logs_) ? ONGOING_LOG
: 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(staged_log_type_ == NO_LOG);
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<unsigned int>(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_ = NO_LOG;
}
void MetricsLogManager::DiscardCurrentLog() {
current_log_->CloseLog();
current_log_.reset();
current_log_type_ = NO_LOG;
}
void MetricsLogManager::PauseCurrentLog() {
DCHECK(!paused_log_.get());
DCHECK(paused_log_type_ == NO_LOG);
paused_log_.reset(current_log_.release());
paused_log_type_ = current_log_type_;
current_log_type_ = NO_LOG;
}
void MetricsLogManager::ResumePausedLog() {
DCHECK(!current_log_.get());
DCHECK(current_log_type_ == NO_LOG);
current_log_.reset(paused_log_.release());
current_log_type_ = paused_log_type_;
paused_log_type_ = 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(log_type != NO_LOG);
std::vector<SerializedLog>* destination_list =
(log_type == 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<SerializedLog>* source_list =
(last_provisional_store_type_ == ONGOING_LOG) ? &unsent_ongoing_logs_
: &unsent_initial_logs_;
DCHECK_LT(static_cast<unsigned int>(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;
// Remove any ongoing logs that are over the serialization size limit.
if (max_ongoing_log_store_size_) {
for (std::vector<SerializedLog>::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<int>(log_size));
it = unsent_ongoing_logs_.erase(it);
} else {
++it;
}
}
}
log_serializer_->SerializeLogs(unsent_initial_logs_, INITIAL_LOG);
log_serializer_->SerializeLogs(unsent_ongoing_logs_, ONGOING_LOG);
}
void MetricsLogManager::LoadPersistedUnsentLogs() {
DCHECK(log_serializer_.get());
if (!log_serializer_.get())
return;
log_serializer_->DeserializeLogs(INITIAL_LOG, &unsent_initial_logs_);
log_serializer_->DeserializeLogs(ONGOING_LOG, &unsent_ongoing_logs_);
}
void MetricsLogManager::CompressCurrentLog(SerializedLog* compressed_log) {
std::string log_text;
current_log_->GetEncodedLog(&log_text);
compressed_log->SwapLogText(&log_text);
}