普通文本  |  218行  |  7.14 KB

// 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 "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<SerializedLog>* 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<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_ = 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<SerializedLog>* 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<SerializedLog>* source_list =
      (last_provisional_store_type_ == MetricsLogBase::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;
  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<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_,
                                 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);
}