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 */ 16 17 #include "uploader/upload_service.h" 18 19 #include <sysexits.h> 20 21 #include <memory> 22 #include <string> 23 24 #include <base/bind.h> 25 #include <base/files/file_util.h> 26 #include <base/logging.h> 27 #include <base/memory/scoped_vector.h> 28 #include <base/message_loop/message_loop.h> 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> 35 36 #include "constants.h" 37 #include "uploader/metrics_log.h" 38 #include "uploader/sender_http.h" 39 #include "uploader/system_profile_setter.h" 40 41 const int UploadService::kMaxFailedUpload = 10; 42 43 UploadService::UploadService(const std::string& server, 44 const base::TimeDelta& upload_interval, 45 const base::TimeDelta& disk_persistence_interval, 46 const base::FilePath& private_metrics_directory, 47 const base::FilePath& shared_metrics_directory) 48 : brillo::Daemon(), 49 histogram_snapshot_manager_(this), 50 sender_(new HttpSender(server)), 51 failed_upload_count_(metrics::kFailedUploadCountName, 52 private_metrics_directory), 53 counters_(new CrashCounters), 54 upload_interval_(upload_interval), 55 disk_persistence_interval_(disk_persistence_interval), 56 metricsd_service_runner_(counters_) { 57 staged_log_path_ = private_metrics_directory.Append(metrics::kStagedLogName); 58 saved_log_path_ = private_metrics_directory.Append(metrics::kSavedLogName); 59 consent_file_ = shared_metrics_directory.Append(metrics::kConsentFileName); 60 } 61 62 void UploadService::LoadSavedLog() { 63 if (base::PathExists(saved_log_path_)) { 64 GetOrCreateCurrentLog()->LoadFromFile(saved_log_path_); 65 } 66 } 67 68 int UploadService::OnInit() { 69 brillo::Daemon::OnInit(); 70 71 base::StatisticsRecorder::Initialize(); 72 metricsd_service_runner_.Start(); 73 74 system_profile_setter_.reset(new SystemProfileCache()); 75 76 base::MessageLoop::current()->PostDelayedTask( 77 FROM_HERE, 78 base::Bind(&UploadService::UploadEventCallback, base::Unretained(this)), 79 upload_interval_); 80 81 base::MessageLoop::current()->PostDelayedTask( 82 FROM_HERE, 83 base::Bind(&UploadService::PersistEventCallback, base::Unretained(this)), 84 disk_persistence_interval_); 85 86 LoadSavedLog(); 87 88 return EX_OK; 89 } 90 91 void UploadService::OnShutdown(int* exit_code) { 92 metricsd_service_runner_.Stop(); 93 PersistToDisk(); 94 } 95 96 void UploadService::InitForTest(SystemProfileSetter* setter) { 97 LoadSavedLog(); 98 system_profile_setter_.reset(setter); 99 } 100 101 void UploadService::StartNewLog() { 102 current_log_.reset(new MetricsLog()); 103 } 104 105 void UploadService::UploadEventCallback() { 106 UploadEvent(); 107 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 } 128 } 129 130 void UploadService::UploadEvent() { 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()) { 136 // Previous upload failed, retry sending the logs. 137 SendStagedLog(); 138 return; 139 } 140 141 // Previous upload successful, stage another log. 142 GatherHistograms(); 143 StageCurrentLog(); 144 145 // If a log is available for upload, upload it. 146 if (HasStagedLog()) { 147 SendStagedLog(); 148 } 149 } 150 151 void UploadService::SendStagedLog() { 152 // If metrics are not enabled, discard the log and exit. 153 if (!AreMetricsEnabled()) { 154 LOG(INFO) << "Metrics disabled. Don't upload metrics samples."; 155 base::DeleteFile(staged_log_path_, false); 156 return; 157 } 158 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"; 167 } else { 168 VLOG(1) << "uploaded " << staged_log.length() << " bytes"; 169 base::DeleteFile(staged_log_path_, false); 170 } 171 172 RemoveFailedLog(); 173 } 174 175 void UploadService::Reset() { 176 base::DeleteFile(staged_log_path_, false); 177 current_log_.reset(); 178 failed_upload_count_.Set(0); 179 } 180 181 void UploadService::GatherHistograms() { 182 base::StatisticsRecorder::Histograms histograms; 183 base::StatisticsRecorder::GetHistograms(&histograms); 184 185 histogram_snapshot_manager_.PrepareDeltas( 186 base::Histogram::kNoFlags, base::Histogram::kUmaTargetedHistogramFlag); 187 188 // Gather and reset the crash counters, shared with the binder threads. 189 unsigned int kernel_crashes = counters_->GetAndResetKernelCrashCount(); 190 unsigned int unclean_shutdowns = counters_->GetAndResetUncleanShutdownCount(); 191 unsigned int user_crashes = counters_->GetAndResetUserCrashCount(); 192 193 // Only create a log if the counters have changed. 194 if (kernel_crashes > 0 || unclean_shutdowns > 0 || user_crashes > 0) { 195 GetOrCreateCurrentLog()->IncrementKernelCrashCount(kernel_crashes); 196 GetOrCreateCurrentLog()->IncrementUncleanShutdownCount(unclean_shutdowns); 197 GetOrCreateCurrentLog()->IncrementUserCrashCount(user_crashes); 198 } 199 } 200 201 void UploadService::RecordDelta(const base::HistogramBase& histogram, 202 const base::HistogramSamples& snapshot) { 203 GetOrCreateCurrentLog()->RecordHistogramDelta(histogram.histogram_name(), 204 snapshot); 205 } 206 207 void UploadService::StageCurrentLog() { 208 // If we haven't logged anything since the last upload, don't upload an empty 209 // report. 210 if (!current_log_) 211 return; 212 213 std::unique_ptr<MetricsLog> staged_log; 214 staged_log.swap(current_log_); 215 staged_log->CloseLog(); 216 if (!staged_log->PopulateSystemProfile(system_profile_setter_.get())) { 217 LOG(WARNING) << "Error while adding metadata to the log. Discarding the " 218 << "log."; 219 return; 220 } 221 222 if (!base::DeleteFile(saved_log_path_, false)) { 223 // There is a chance that we will upload the same metrics twice but, if we 224 // are lucky, the backup should be overridden before that. In doubt, try not 225 // to lose any metrics. 226 LOG(ERROR) << "failed to delete the last backup of the current log."; 227 } 228 229 failed_upload_count_.Set(0); 230 staged_log->SaveToFile(staged_log_path_); 231 } 232 233 MetricsLog* UploadService::GetOrCreateCurrentLog() { 234 if (!current_log_) { 235 StartNewLog(); 236 } 237 return current_log_.get(); 238 } 239 240 bool UploadService::HasStagedLog() { 241 return base::PathExists(staged_log_path_); 242 } 243 244 void UploadService::RemoveFailedLog() { 245 if (failed_upload_count_.Get() > kMaxFailedUpload) { 246 LOG(INFO) << "log failed more than " << kMaxFailedUpload << " times."; 247 CHECK(base::DeleteFile(staged_log_path_, false)) 248 << "failed to delete staged log at " << staged_log_path_.value(); 249 failed_upload_count_.Set(0); 250 } 251 } 252 253 bool UploadService::AreMetricsEnabled() { 254 return base::PathExists(consent_file_); 255 } 256