Bertrand SIMONNET | 52e5b99 | 2015-08-10 15:18:00 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2015 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 16 | |
Bertrand SIMONNET | 4b915ae | 2015-07-28 15:38:14 -0700 | [diff] [blame] | 17 | #include "uploader/upload_service.h" |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 18 | |
Bertrand SIMONNET | 608e428 | 2015-11-12 17:52:17 -0800 | [diff] [blame] | 19 | #include <sysexits.h> |
| 20 | |
Bertrand SIMONNET | 6c9fbb9 | 2015-12-21 14:56:40 -0800 | [diff] [blame] | 21 | #include <memory> |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 22 | #include <string> |
| 23 | |
Steve Fung | ae4bdc4 | 2015-01-26 17:13:24 -0800 | [diff] [blame] | 24 | #include <base/bind.h> |
Bertrand SIMONNET | 1df10c4 | 2015-09-09 10:39:51 -0700 | [diff] [blame] | 25 | #include <base/files/file_util.h> |
Alex Vakulenko | ec991df | 2014-09-04 16:16:28 -0700 | [diff] [blame] | 26 | #include <base/logging.h> |
| 27 | #include <base/memory/scoped_vector.h> |
Steve Fung | ae4bdc4 | 2015-01-26 17:13:24 -0800 | [diff] [blame] | 28 | #include <base/message_loop/message_loop.h> |
Alex Vakulenko | ec991df | 2014-09-04 16:16:28 -0700 | [diff] [blame] | 29 | #include <base/metrics/histogram.h> |
| 30 | #include <base/metrics/histogram_base.h> |
| 31 | #include <base/metrics/histogram_snapshot_manager.h> |
| 32 | #include <base/metrics/sparse_histogram.h> |
| 33 | #include <base/metrics/statistics_recorder.h> |
| 34 | #include <base/sha1.h> |
Alex Vakulenko | ec991df | 2014-09-04 16:16:28 -0700 | [diff] [blame] | 35 | |
Bertrand SIMONNET | 2765d0a | 2015-09-09 10:38:20 -0700 | [diff] [blame] | 36 | #include "constants.h" |
Bertrand SIMONNET | 4b915ae | 2015-07-28 15:38:14 -0700 | [diff] [blame] | 37 | #include "uploader/metrics_log.h" |
| 38 | #include "uploader/sender_http.h" |
Bertrand SIMONNET | 52e1d55 | 2015-08-04 15:06:21 -0700 | [diff] [blame] | 39 | #include "uploader/system_profile_setter.h" |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 40 | |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 41 | const int UploadService::kMaxFailedUpload = 10; |
| 42 | |
Bertrand SIMONNET | 608e428 | 2015-11-12 17:52:17 -0800 | [diff] [blame] | 43 | UploadService::UploadService(const std::string& server, |
| 44 | const base::TimeDelta& upload_interval, |
Bertrand SIMONNET | 0586504 | 2015-12-15 12:33:01 -0800 | [diff] [blame] | 45 | const base::TimeDelta& disk_persistence_interval, |
Bertrand SIMONNET | 9d3a4ae | 2015-11-25 13:49:12 -0800 | [diff] [blame] | 46 | const base::FilePath& private_metrics_directory, |
Bertrand SIMONNET | b6c77af | 2015-12-08 17:46:00 -0800 | [diff] [blame] | 47 | const base::FilePath& shared_metrics_directory) |
| 48 | : brillo::Daemon(), |
| 49 | histogram_snapshot_manager_(this), |
Steve Fung | ae4bdc4 | 2015-01-26 17:13:24 -0800 | [diff] [blame] | 50 | sender_(new HttpSender(server)), |
Bertrand SIMONNET | 9d3a4ae | 2015-11-25 13:49:12 -0800 | [diff] [blame] | 51 | failed_upload_count_(metrics::kFailedUploadCountName, |
| 52 | private_metrics_directory), |
Bertrand SIMONNET | b6c77af | 2015-12-08 17:46:00 -0800 | [diff] [blame] | 53 | counters_(new CrashCounters), |
| 54 | upload_interval_(upload_interval), |
Bertrand SIMONNET | 0586504 | 2015-12-15 12:33:01 -0800 | [diff] [blame] | 55 | disk_persistence_interval_(disk_persistence_interval), |
Bertrand SIMONNET | b6c77af | 2015-12-08 17:46:00 -0800 | [diff] [blame] | 56 | metricsd_service_runner_(counters_) { |
Bertrand SIMONNET | 9d3a4ae | 2015-11-25 13:49:12 -0800 | [diff] [blame] | 57 | staged_log_path_ = private_metrics_directory.Append(metrics::kStagedLogName); |
Bertrand SIMONNET | 0586504 | 2015-12-15 12:33:01 -0800 | [diff] [blame] | 58 | saved_log_path_ = private_metrics_directory.Append(metrics::kSavedLogName); |
Bertrand SIMONNET | 9d3a4ae | 2015-11-25 13:49:12 -0800 | [diff] [blame] | 59 | consent_file_ = shared_metrics_directory.Append(metrics::kConsentFileName); |
Bertrand SIMONNET | 608e428 | 2015-11-12 17:52:17 -0800 | [diff] [blame] | 60 | } |
Steve Fung | ae4bdc4 | 2015-01-26 17:13:24 -0800 | [diff] [blame] | 61 | |
Bertrand SIMONNET | 0586504 | 2015-12-15 12:33:01 -0800 | [diff] [blame] | 62 | void UploadService::LoadSavedLog() { |
| 63 | if (base::PathExists(saved_log_path_)) { |
| 64 | GetOrCreateCurrentLog()->LoadFromFile(saved_log_path_); |
| 65 | } |
| 66 | } |
| 67 | |
Bertrand SIMONNET | 608e428 | 2015-11-12 17:52:17 -0800 | [diff] [blame] | 68 | int UploadService::OnInit() { |
Bertrand SIMONNET | b6c77af | 2015-12-08 17:46:00 -0800 | [diff] [blame] | 69 | brillo::Daemon::OnInit(); |
| 70 | |
| 71 | base::StatisticsRecorder::Initialize(); |
| 72 | metricsd_service_runner_.Start(); |
| 73 | |
Bertrand SIMONNET | 608e428 | 2015-11-12 17:52:17 -0800 | [diff] [blame] | 74 | system_profile_setter_.reset(new SystemProfileCache()); |
| 75 | |
Bertrand SIMONNET | 0586504 | 2015-12-15 12:33:01 -0800 | [diff] [blame] | 76 | base::MessageLoop::current()->PostDelayedTask( |
| 77 | FROM_HERE, |
| 78 | base::Bind(&UploadService::UploadEventCallback, base::Unretained(this)), |
Bertrand SIMONNET | 608e428 | 2015-11-12 17:52:17 -0800 | [diff] [blame] | 79 | upload_interval_); |
Bertrand SIMONNET | b6c77af | 2015-12-08 17:46:00 -0800 | [diff] [blame] | 80 | |
Bertrand SIMONNET | 0586504 | 2015-12-15 12:33:01 -0800 | [diff] [blame] | 81 | base::MessageLoop::current()->PostDelayedTask( |
| 82 | FROM_HERE, |
| 83 | base::Bind(&UploadService::PersistEventCallback, base::Unretained(this)), |
| 84 | disk_persistence_interval_); |
| 85 | |
| 86 | LoadSavedLog(); |
| 87 | |
Bertrand SIMONNET | 608e428 | 2015-11-12 17:52:17 -0800 | [diff] [blame] | 88 | return EX_OK; |
| 89 | } |
| 90 | |
Bertrand SIMONNET | b6c77af | 2015-12-08 17:46:00 -0800 | [diff] [blame] | 91 | void UploadService::OnShutdown(int* exit_code) { |
| 92 | metricsd_service_runner_.Stop(); |
Bertrand SIMONNET | d686231 | 2016-01-19 14:04:25 -0800 | [diff] [blame] | 93 | PersistToDisk(); |
Bertrand SIMONNET | b6c77af | 2015-12-08 17:46:00 -0800 | [diff] [blame] | 94 | } |
| 95 | |
Bertrand SIMONNET | 608e428 | 2015-11-12 17:52:17 -0800 | [diff] [blame] | 96 | void UploadService::InitForTest(SystemProfileSetter* setter) { |
Bertrand SIMONNET | 0586504 | 2015-12-15 12:33:01 -0800 | [diff] [blame] | 97 | LoadSavedLog(); |
Bertrand SIMONNET | 608e428 | 2015-11-12 17:52:17 -0800 | [diff] [blame] | 98 | system_profile_setter_.reset(setter); |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 99 | } |
| 100 | |
| 101 | void UploadService::StartNewLog() { |
Bertrand SIMONNET | 0586504 | 2015-12-15 12:33:01 -0800 | [diff] [blame] | 102 | current_log_.reset(new MetricsLog()); |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 103 | } |
| 104 | |
Bertrand SIMONNET | 0586504 | 2015-12-15 12:33:01 -0800 | [diff] [blame] | 105 | void UploadService::UploadEventCallback() { |
Steve Fung | ae4bdc4 | 2015-01-26 17:13:24 -0800 | [diff] [blame] | 106 | UploadEvent(); |
| 107 | |
Bertrand SIMONNET | 0586504 | 2015-12-15 12:33:01 -0800 | [diff] [blame] | 108 | base::MessageLoop::current()->PostDelayedTask( |
| 109 | FROM_HERE, |
| 110 | base::Bind(&UploadService::UploadEventCallback, base::Unretained(this)), |
| 111 | upload_interval_); |
| 112 | } |
| 113 | |
| 114 | void UploadService::PersistEventCallback() { |
| 115 | PersistToDisk(); |
| 116 | |
| 117 | base::MessageLoop::current()->PostDelayedTask( |
| 118 | FROM_HERE, |
| 119 | base::Bind(&UploadService::PersistEventCallback, base::Unretained(this)), |
| 120 | disk_persistence_interval_); |
| 121 | } |
| 122 | |
| 123 | void UploadService::PersistToDisk() { |
| 124 | GatherHistograms(); |
| 125 | if (current_log_) { |
| 126 | current_log_->SaveToFile(saved_log_path_); |
| 127 | } |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 128 | } |
| 129 | |
| 130 | void UploadService::UploadEvent() { |
Bertrand SIMONNET | 1df10c4 | 2015-09-09 10:39:51 -0700 | [diff] [blame] | 131 | // If the system shutdown or crashed while uploading a report, we may not have |
| 132 | // deleted an old log. |
| 133 | RemoveFailedLog(); |
| 134 | |
| 135 | if (HasStagedLog()) { |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 136 | // Previous upload failed, retry sending the logs. |
| 137 | SendStagedLog(); |
| 138 | return; |
| 139 | } |
| 140 | |
Bertrand SIMONNET | 6b8629a | 2015-11-18 13:46:33 -0800 | [diff] [blame] | 141 | // Previous upload successful, stage another log. |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 142 | GatherHistograms(); |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 143 | StageCurrentLog(); |
Bertrand SIMONNET | 1f14655 | 2015-08-19 18:13:02 -0700 | [diff] [blame] | 144 | |
| 145 | // If a log is available for upload, upload it. |
Bertrand SIMONNET | 1df10c4 | 2015-09-09 10:39:51 -0700 | [diff] [blame] | 146 | if (HasStagedLog()) { |
Bertrand SIMONNET | 1f14655 | 2015-08-19 18:13:02 -0700 | [diff] [blame] | 147 | SendStagedLog(); |
| 148 | } |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 149 | } |
| 150 | |
| 151 | void UploadService::SendStagedLog() { |
Bertrand SIMONNET | e4fa61e | 2015-02-18 09:38:55 -0800 | [diff] [blame] | 152 | // If metrics are not enabled, discard the log and exit. |
Bertrand SIMONNET | 608e428 | 2015-11-12 17:52:17 -0800 | [diff] [blame] | 153 | if (!AreMetricsEnabled()) { |
Bertrand SIMONNET | e4fa61e | 2015-02-18 09:38:55 -0800 | [diff] [blame] | 154 | LOG(INFO) << "Metrics disabled. Don't upload metrics samples."; |
Bertrand SIMONNET | 1df10c4 | 2015-09-09 10:39:51 -0700 | [diff] [blame] | 155 | base::DeleteFile(staged_log_path_, false); |
Bertrand SIMONNET | e4fa61e | 2015-02-18 09:38:55 -0800 | [diff] [blame] | 156 | return; |
| 157 | } |
| 158 | |
Bertrand SIMONNET | 1df10c4 | 2015-09-09 10:39:51 -0700 | [diff] [blame] | 159 | std::string staged_log; |
| 160 | CHECK(base::ReadFileToString(staged_log_path_, &staged_log)); |
| 161 | |
| 162 | // Increase the failed count in case the daemon crashes while sending the log. |
| 163 | failed_upload_count_.Add(1); |
| 164 | |
| 165 | if (!sender_->Send(staged_log, base::SHA1HashString(staged_log))) { |
| 166 | LOG(WARNING) << "log failed to upload"; |
Nathan Bullock | 0be0f73 | 2014-09-11 10:29:17 -0400 | [diff] [blame] | 167 | } else { |
Bertrand SIMONNET | 1df10c4 | 2015-09-09 10:39:51 -0700 | [diff] [blame] | 168 | VLOG(1) << "uploaded " << staged_log.length() << " bytes"; |
| 169 | base::DeleteFile(staged_log_path_, false); |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 170 | } |
Bertrand SIMONNET | 1df10c4 | 2015-09-09 10:39:51 -0700 | [diff] [blame] | 171 | |
| 172 | RemoveFailedLog(); |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 173 | } |
| 174 | |
| 175 | void UploadService::Reset() { |
Bertrand SIMONNET | 1df10c4 | 2015-09-09 10:39:51 -0700 | [diff] [blame] | 176 | base::DeleteFile(staged_log_path_, false); |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 177 | current_log_.reset(); |
Bertrand SIMONNET | 1df10c4 | 2015-09-09 10:39:51 -0700 | [diff] [blame] | 178 | failed_upload_count_.Set(0); |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 179 | } |
| 180 | |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 181 | void UploadService::GatherHistograms() { |
| 182 | base::StatisticsRecorder::Histograms histograms; |
| 183 | base::StatisticsRecorder::GetHistograms(&histograms); |
| 184 | |
| 185 | histogram_snapshot_manager_.PrepareDeltas( |
Alex Vakulenko | 3d97929 | 2016-03-17 14:29:26 -0700 | [diff] [blame] | 186 | histograms.begin(), histograms.end(), |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 187 | base::Histogram::kNoFlags, base::Histogram::kUmaTargetedHistogramFlag); |
Bertrand SIMONNET | 6b8629a | 2015-11-18 13:46:33 -0800 | [diff] [blame] | 188 | |
| 189 | // Gather and reset the crash counters, shared with the binder threads. |
| 190 | unsigned int kernel_crashes = counters_->GetAndResetKernelCrashCount(); |
| 191 | unsigned int unclean_shutdowns = counters_->GetAndResetUncleanShutdownCount(); |
| 192 | unsigned int user_crashes = counters_->GetAndResetUserCrashCount(); |
| 193 | |
| 194 | // Only create a log if the counters have changed. |
| 195 | if (kernel_crashes > 0 || unclean_shutdowns > 0 || user_crashes > 0) { |
| 196 | GetOrCreateCurrentLog()->IncrementKernelCrashCount(kernel_crashes); |
| 197 | GetOrCreateCurrentLog()->IncrementUncleanShutdownCount(unclean_shutdowns); |
| 198 | GetOrCreateCurrentLog()->IncrementUserCrashCount(user_crashes); |
| 199 | } |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 200 | } |
| 201 | |
| 202 | void UploadService::RecordDelta(const base::HistogramBase& histogram, |
| 203 | const base::HistogramSamples& snapshot) { |
| 204 | GetOrCreateCurrentLog()->RecordHistogramDelta(histogram.histogram_name(), |
| 205 | snapshot); |
| 206 | } |
| 207 | |
| 208 | void UploadService::StageCurrentLog() { |
Bertrand SIMONNET | 1df10c4 | 2015-09-09 10:39:51 -0700 | [diff] [blame] | 209 | // If we haven't logged anything since the last upload, don't upload an empty |
| 210 | // report. |
| 211 | if (!current_log_) |
| 212 | return; |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 213 | |
Bertrand SIMONNET | 6c9fbb9 | 2015-12-21 14:56:40 -0800 | [diff] [blame] | 214 | std::unique_ptr<MetricsLog> staged_log; |
Bertrand SIMONNET | 1df10c4 | 2015-09-09 10:39:51 -0700 | [diff] [blame] | 215 | staged_log.swap(current_log_); |
| 216 | staged_log->CloseLog(); |
| 217 | if (!staged_log->PopulateSystemProfile(system_profile_setter_.get())) { |
Bertrand SIMONNET | 1f14655 | 2015-08-19 18:13:02 -0700 | [diff] [blame] | 218 | LOG(WARNING) << "Error while adding metadata to the log. Discarding the " |
| 219 | << "log."; |
Bertrand SIMONNET | 1df10c4 | 2015-09-09 10:39:51 -0700 | [diff] [blame] | 220 | return; |
Bertrand SIMONNET | 1f14655 | 2015-08-19 18:13:02 -0700 | [diff] [blame] | 221 | } |
Bertrand SIMONNET | 0586504 | 2015-12-15 12:33:01 -0800 | [diff] [blame] | 222 | |
| 223 | if (!base::DeleteFile(saved_log_path_, false)) { |
| 224 | // There is a chance that we will upload the same metrics twice but, if we |
| 225 | // are lucky, the backup should be overridden before that. In doubt, try not |
| 226 | // to lose any metrics. |
| 227 | LOG(ERROR) << "failed to delete the last backup of the current log."; |
| 228 | } |
Bertrand SIMONNET | 1df10c4 | 2015-09-09 10:39:51 -0700 | [diff] [blame] | 229 | |
| 230 | failed_upload_count_.Set(0); |
Bertrand SIMONNET | 0586504 | 2015-12-15 12:33:01 -0800 | [diff] [blame] | 231 | staged_log->SaveToFile(staged_log_path_); |
Bertrand SIMONNET | 46b49da | 2014-06-25 14:38:07 -0700 | [diff] [blame] | 232 | } |
| 233 | |
| 234 | MetricsLog* UploadService::GetOrCreateCurrentLog() { |
| 235 | if (!current_log_) { |
| 236 | StartNewLog(); |
| 237 | } |
| 238 | return current_log_.get(); |
| 239 | } |
Bertrand SIMONNET | 1df10c4 | 2015-09-09 10:39:51 -0700 | [diff] [blame] | 240 | |
| 241 | bool UploadService::HasStagedLog() { |
| 242 | return base::PathExists(staged_log_path_); |
| 243 | } |
| 244 | |
| 245 | void UploadService::RemoveFailedLog() { |
| 246 | if (failed_upload_count_.Get() > kMaxFailedUpload) { |
| 247 | LOG(INFO) << "log failed more than " << kMaxFailedUpload << " times."; |
| 248 | CHECK(base::DeleteFile(staged_log_path_, false)) |
| 249 | << "failed to delete staged log at " << staged_log_path_.value(); |
| 250 | failed_upload_count_.Set(0); |
| 251 | } |
| 252 | } |
Bertrand SIMONNET | 608e428 | 2015-11-12 17:52:17 -0800 | [diff] [blame] | 253 | |
| 254 | bool UploadService::AreMetricsEnabled() { |
| 255 | return base::PathExists(consent_file_); |
| 256 | } |