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 |