| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/common/metrics/metrics_log_manager.h" | 5 #include "chrome/common/metrics/metrics_log_manager.h" |
| 6 | 6 |
| 7 #if defined(USE_SYSTEM_LIBBZ2) | 7 #if defined(USE_SYSTEM_LIBBZ2) |
| 8 #include <bzlib.h> | 8 #include <bzlib.h> |
| 9 #else | 9 #else |
| 10 #include "third_party/bzip2/bzlib.h" | 10 #include "third_party/bzip2/bzlib.h" |
| 11 #endif | 11 #endif |
| 12 | 12 |
| 13 #include "base/metrics/histogram.h" | 13 #include "base/metrics/histogram.h" |
| 14 #include "base/string_util.h" | 14 #include "base/string_util.h" |
| 15 #include "chrome/common/metrics/metrics_log_base.h" | 15 #include "chrome/common/metrics/metrics_log_base.h" |
| 16 | 16 |
| 17 namespace { | 17 namespace { |
| 18 | 18 |
| 19 // Used to keep track of discarded protobuf logs without having to track xml and | 19 // Used to keep track of discarded protobuf logs without having to track xml and |
| 20 // protobuf logs in separate lists. | 20 // protobuf logs in separate lists. |
| 21 const char kDiscardedLog[] = "Log discarded"; | 21 const char kDiscardedLog[] = "Log discarded"; |
| 22 | 22 |
| 23 } // anonymous namespace | 23 } // anonymous namespace |
| 24 | 24 |
| 25 MetricsLogManager::MetricsLogManager() : max_ongoing_log_store_size_(0) {} | 25 MetricsLogManager::MetricsLogManager() : current_log_type_(INITIAL_LOG), |
| 26 staged_log_type_(INITIAL_LOG), |
| 27 max_ongoing_log_store_size_(0) {} |
| 26 | 28 |
| 27 MetricsLogManager::~MetricsLogManager() {} | 29 MetricsLogManager::~MetricsLogManager() {} |
| 28 | 30 |
| 29 bool MetricsLogManager::SerializedLog::empty() const { | 31 bool MetricsLogManager::SerializedLog::empty() const { |
| 30 DCHECK_EQ(xml.empty(), proto.empty()); | 32 DCHECK_EQ(xml.empty(), proto.empty()); |
| 31 return xml.empty(); | 33 return xml.empty(); |
| 32 } | 34 } |
| 33 | 35 |
| 34 void MetricsLogManager::BeginLoggingWithLog(MetricsLogBase* log) { | 36 void MetricsLogManager::SerializedLog::swap(SerializedLog& log) { |
| 37 xml.swap(log.xml); |
| 38 proto.swap(log.proto); |
| 39 } |
| 40 |
| 41 void MetricsLogManager::BeginLoggingWithLog(MetricsLogBase* log, |
| 42 LogType log_type) { |
| 35 DCHECK(!current_log_.get()); | 43 DCHECK(!current_log_.get()); |
| 36 current_log_.reset(log); | 44 current_log_.reset(log); |
| 45 current_log_type_ = log_type; |
| 37 } | 46 } |
| 38 | 47 |
| 39 void MetricsLogManager::StageCurrentLogForUpload() { | 48 void MetricsLogManager::FinishCurrentLog() { |
| 40 DCHECK(current_log_.get()); | 49 DCHECK(current_log_.get()); |
| 41 current_log_->CloseLog(); | 50 current_log_->CloseLog(); |
| 42 staged_log_.reset(current_log_.release()); | 51 SerializedLog compressed_log; |
| 43 CompressStagedLog(); | 52 CompressCurrentLog(&compressed_log); |
| 53 if (!compressed_log.empty()) |
| 54 StoreLog(&compressed_log, current_log_type_); |
| 55 current_log_.reset(); |
| 56 } |
| 57 |
| 58 void MetricsLogManager::StageNextLogForUpload() { |
| 59 // Prioritize initial logs for uploading. |
| 60 std::vector<SerializedLog>* source_list = |
| 61 unsent_initial_logs_.empty() ? &unsent_ongoing_logs_ |
| 62 : &unsent_initial_logs_; |
| 63 DCHECK(!source_list->empty()); |
| 64 DCHECK(staged_log_text_.empty()); |
| 65 staged_log_text_.swap(source_list->back()); |
| 66 source_list->pop_back(); |
| 44 } | 67 } |
| 45 | 68 |
| 46 bool MetricsLogManager::has_staged_log() const { | 69 bool MetricsLogManager::has_staged_log() const { |
| 47 return staged_log_.get() || !staged_log_text().empty(); | 70 return !staged_log_text().empty(); |
| 48 } | 71 } |
| 49 | 72 |
| 50 bool MetricsLogManager::has_staged_log_proto() const { | 73 bool MetricsLogManager::has_staged_log_proto() const { |
| 51 return has_staged_log() && staged_log_text().proto != kDiscardedLog; | 74 return has_staged_log() && staged_log_text().proto != kDiscardedLog; |
| 52 } | 75 } |
| 53 | 76 |
| 54 void MetricsLogManager::DiscardStagedLog() { | 77 void MetricsLogManager::DiscardStagedLog() { |
| 55 staged_log_.reset(); | |
| 56 staged_log_text_.xml.clear(); | 78 staged_log_text_.xml.clear(); |
| 57 staged_log_text_.proto.clear(); | 79 staged_log_text_.proto.clear(); |
| 58 } | 80 } |
| 59 | 81 |
| 60 void MetricsLogManager::DiscardStagedLogProto() { | 82 void MetricsLogManager::DiscardStagedLogProto() { |
| 61 staged_log_text_.proto = kDiscardedLog; | 83 staged_log_text_.proto = kDiscardedLog; |
| 62 } | 84 } |
| 63 | 85 |
| 64 void MetricsLogManager::DiscardCurrentLog() { | 86 void MetricsLogManager::DiscardCurrentLog() { |
| 65 current_log_->CloseLog(); | 87 current_log_->CloseLog(); |
| 66 current_log_.reset(); | 88 current_log_.reset(); |
| 67 } | 89 } |
| 68 | 90 |
| 69 void MetricsLogManager::PauseCurrentLog() { | 91 void MetricsLogManager::PauseCurrentLog() { |
| 70 DCHECK(!paused_log_.get()); | 92 DCHECK(!paused_log_.get()); |
| 71 paused_log_.reset(current_log_.release()); | 93 paused_log_.reset(current_log_.release()); |
| 72 } | 94 } |
| 73 | 95 |
| 74 void MetricsLogManager::ResumePausedLog() { | 96 void MetricsLogManager::ResumePausedLog() { |
| 75 DCHECK(!current_log_.get()); | 97 DCHECK(!current_log_.get()); |
| 76 current_log_.reset(paused_log_.release()); | 98 current_log_.reset(paused_log_.release()); |
| 77 } | 99 } |
| 78 | 100 |
| 79 void MetricsLogManager::StoreStagedLogAsUnsent(LogType log_type) { | 101 void MetricsLogManager::StoreStagedLogAsUnsent() { |
| 80 DCHECK(has_staged_log()); | 102 DCHECK(has_staged_log()); |
| 81 | 103 |
| 82 // If compressing the log failed, there's nothing to store. | 104 // If compressing the log failed, there's nothing to store. |
| 83 if (staged_log_text().empty()) | 105 if (staged_log_text_.empty()) |
| 84 return; | 106 return; |
| 85 | 107 |
| 86 if (log_type == INITIAL_LOG) { | 108 StoreLog(&staged_log_text_, staged_log_type_); |
| 87 unsent_initial_logs_.push_back(staged_log_text_); | |
| 88 } else { | |
| 89 // If it's too large, just note that and discard it. | |
| 90 if (max_ongoing_log_store_size_ && | |
| 91 staged_log_text().xml.length() > max_ongoing_log_store_size_) { | |
| 92 // TODO(isherman): We probably want a similar check for protobufs, but we | |
| 93 // don't want to prevent XML upload just because the protobuf version is | |
| 94 // too long. In practice, I'm pretty sure the XML version should always | |
| 95 // be longer, or at least on the same order of magnitude in length. | |
| 96 UMA_HISTOGRAM_COUNTS( | |
| 97 "UMA.Large Accumulated Log Not Persisted", | |
| 98 static_cast<int>(staged_log_text().xml.length())); | |
| 99 } else { | |
| 100 unsent_ongoing_logs_.push_back(staged_log_text_); | |
| 101 } | |
| 102 } | |
| 103 DiscardStagedLog(); | 109 DiscardStagedLog(); |
| 104 } | 110 } |
| 105 | 111 |
| 106 void MetricsLogManager::StageNextStoredLogForUpload() { | 112 void MetricsLogManager::StoreLog(SerializedLog* log_text, LogType log_type) { |
| 107 // Prioritize initial logs for uploading. | 113 std::vector<SerializedLog>* destination_list = |
| 108 std::vector<SerializedLog>* source_list = | 114 (log_type == INITIAL_LOG) ? &unsent_initial_logs_ |
| 109 unsent_initial_logs_.empty() ? | 115 : &unsent_ongoing_logs_; |
| 110 &unsent_ongoing_logs_ : | 116 destination_list->push_back(SerializedLog()); |
| 111 &unsent_initial_logs_; | 117 destination_list->back().swap(*log_text); |
| 112 DCHECK(!source_list->empty()); | |
| 113 DCHECK(staged_log_text().empty()); | |
| 114 staged_log_text_ = source_list->back(); | |
| 115 source_list->pop_back(); | |
| 116 } | 118 } |
| 117 | 119 |
| 118 void MetricsLogManager::PersistUnsentLogs() { | 120 void MetricsLogManager::PersistUnsentLogs() { |
| 119 DCHECK(log_serializer_.get()); | 121 DCHECK(log_serializer_.get()); |
| 120 if (!log_serializer_.get()) | 122 if (!log_serializer_.get()) |
| 121 return; | 123 return; |
| 124 // Remove any ongoing logs that are over the serialization size limit. |
| 125 if (max_ongoing_log_store_size_) { |
| 126 for (std::vector<SerializedLog>::iterator it = unsent_ongoing_logs_.begin(); |
| 127 it != unsent_ongoing_logs_.end();) { |
| 128 size_t log_size = it->xml.length(); |
| 129 if (log_size > max_ongoing_log_store_size_) { |
| 130 // TODO(isherman): We probably want a similar check for protobufs, but |
| 131 // we don't want to prevent XML upload just because the protobuf version |
| 132 // is too long. In practice, I'm pretty sure the XML version should |
| 133 // always be longer, or at least on the same order of magnitude in |
| 134 // length. |
| 135 UMA_HISTOGRAM_COUNTS("UMA.Large Accumulated Log Not Persisted", |
| 136 static_cast<int>(log_size)); |
| 137 it = unsent_ongoing_logs_.erase(it); |
| 138 } else { |
| 139 ++it; |
| 140 } |
| 141 } |
| 142 } |
| 122 log_serializer_->SerializeLogs(unsent_initial_logs_, INITIAL_LOG); | 143 log_serializer_->SerializeLogs(unsent_initial_logs_, INITIAL_LOG); |
| 123 log_serializer_->SerializeLogs(unsent_ongoing_logs_, ONGOING_LOG); | 144 log_serializer_->SerializeLogs(unsent_ongoing_logs_, ONGOING_LOG); |
| 124 } | 145 } |
| 125 | 146 |
| 126 void MetricsLogManager::LoadPersistedUnsentLogs() { | 147 void MetricsLogManager::LoadPersistedUnsentLogs() { |
| 127 DCHECK(log_serializer_.get()); | 148 DCHECK(log_serializer_.get()); |
| 128 if (!log_serializer_.get()) | 149 if (!log_serializer_.get()) |
| 129 return; | 150 return; |
| 130 log_serializer_->DeserializeLogs(INITIAL_LOG, &unsent_initial_logs_); | 151 log_serializer_->DeserializeLogs(INITIAL_LOG, &unsent_initial_logs_); |
| 131 log_serializer_->DeserializeLogs(ONGOING_LOG, &unsent_ongoing_logs_); | 152 log_serializer_->DeserializeLogs(ONGOING_LOG, &unsent_ongoing_logs_); |
| 132 } | 153 } |
| 133 | 154 |
| 134 void MetricsLogManager::CompressStagedLog() { | 155 void MetricsLogManager::CompressCurrentLog(SerializedLog* compressed_log) { |
| 135 int text_size = staged_log_->GetEncodedLogSizeXml(); | 156 int text_size = current_log_->GetEncodedLogSizeXml(); |
| 136 std::string staged_log_text; | |
| 137 DCHECK_GT(text_size, 0); | 157 DCHECK_GT(text_size, 0); |
| 138 staged_log_->GetEncodedLogXml(WriteInto(&staged_log_text, text_size + 1), | 158 std::string log_text; |
| 139 text_size); | 159 current_log_->GetEncodedLogXml(WriteInto(&log_text, text_size + 1), |
| 160 text_size); |
| 140 | 161 |
| 141 bool success = Bzip2Compress(staged_log_text, &staged_log_text_.xml); | 162 bool success = Bzip2Compress(log_text, &(compressed_log->xml)); |
| 142 if (success) { | 163 if (success) { |
| 143 // Allow security-conscious users to see all metrics logs that we send. | 164 // Allow security-conscious users to see all metrics logs that we send. |
| 144 DVLOG(1) << "METRICS LOG: " << staged_log_text; | 165 DVLOG(1) << "METRICS LOG: " << log_text; |
| 145 | 166 |
| 146 // Note that we only save the protobuf version if we succeeded in | 167 // Note that we only save the protobuf version if we succeeded in |
| 147 // compressing the XML, so that the two data streams are the same. | 168 // compressing the XML, so that the two data streams are the same. |
| 148 staged_log_->GetEncodedLogProto(&staged_log_text_.proto); | 169 current_log_->GetEncodedLogProto(&(compressed_log->proto)); |
| 149 } else { | 170 } else { |
| 150 NOTREACHED() << "Failed to compress log for transmission."; | 171 NOTREACHED() << "Failed to compress log for transmission."; |
| 151 } | 172 } |
| 152 } | 173 } |
| 153 | 174 |
| 154 // static | 175 // static |
| 155 // This implementation is based on the Firefox MetricsService implementation. | 176 // This implementation is based on the Firefox MetricsService implementation. |
| 156 bool MetricsLogManager::Bzip2Compress(const std::string& input, | 177 bool MetricsLogManager::Bzip2Compress(const std::string& input, |
| 157 std::string* output) { | 178 std::string* output) { |
| 158 bz_stream stream = {0}; | 179 bz_stream stream = {0}; |
| (...skipping 27 matching lines...) Expand all Loading... |
| 186 // TODO(jar): See if it would be better to do a CHECK() here. | 207 // TODO(jar): See if it would be better to do a CHECK() here. |
| 187 return false; | 208 return false; |
| 188 } | 209 } |
| 189 result = BZ2_bzCompressEnd(&stream); | 210 result = BZ2_bzCompressEnd(&stream); |
| 190 DCHECK(result == BZ_OK); | 211 DCHECK(result == BZ_OK); |
| 191 | 212 |
| 192 output->resize(stream.total_out_lo32); | 213 output->resize(stream.total_out_lo32); |
| 193 | 214 |
| 194 return true; | 215 return true; |
| 195 } | 216 } |
| OLD | NEW |