/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "uploader/upload_service.h" #include <sysexits.h> #include <memory> #include <string> #include <base/bind.h> #include <base/files/file_util.h> #include <base/logging.h> #include <base/memory/scoped_vector.h> #include <base/message_loop/message_loop.h> #include <base/metrics/histogram.h> #include <base/metrics/histogram_base.h> #include <base/metrics/histogram_snapshot_manager.h> #include <base/metrics/sparse_histogram.h> #include <base/metrics/statistics_recorder.h> #include <base/sha1.h> #include "constants.h" #include "uploader/metrics_log.h" #include "uploader/sender_http.h" #include "uploader/system_profile_setter.h" const int UploadService::kMaxFailedUpload = 10; UploadService::UploadService(const std::string& server, const base::TimeDelta& upload_interval, const base::TimeDelta& disk_persistence_interval, const base::FilePath& private_metrics_directory, const base::FilePath& shared_metrics_directory) : brillo::Daemon(), histogram_snapshot_manager_(this), sender_(new HttpSender(server)), failed_upload_count_(metrics::kFailedUploadCountName, private_metrics_directory), counters_(new CrashCounters), upload_interval_(upload_interval), disk_persistence_interval_(disk_persistence_interval), metricsd_service_runner_(counters_) { staged_log_path_ = private_metrics_directory.Append(metrics::kStagedLogName); saved_log_path_ = private_metrics_directory.Append(metrics::kSavedLogName); consent_file_ = shared_metrics_directory.Append(metrics::kConsentFileName); } void UploadService::LoadSavedLog() { if (base::PathExists(saved_log_path_)) { GetOrCreateCurrentLog()->LoadFromFile(saved_log_path_); } } int UploadService::OnInit() { brillo::Daemon::OnInit(); base::StatisticsRecorder::Initialize(); metricsd_service_runner_.Start(); system_profile_setter_.reset(new SystemProfileCache()); base::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&UploadService::UploadEventCallback, base::Unretained(this)), upload_interval_); base::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&UploadService::PersistEventCallback, base::Unretained(this)), disk_persistence_interval_); LoadSavedLog(); return EX_OK; } void UploadService::OnShutdown(int* exit_code) { metricsd_service_runner_.Stop(); PersistToDisk(); } void UploadService::InitForTest(SystemProfileSetter* setter) { LoadSavedLog(); system_profile_setter_.reset(setter); } void UploadService::StartNewLog() { current_log_.reset(new MetricsLog()); } void UploadService::UploadEventCallback() { UploadEvent(); base::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&UploadService::UploadEventCallback, base::Unretained(this)), upload_interval_); } void UploadService::PersistEventCallback() { PersistToDisk(); base::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&UploadService::PersistEventCallback, base::Unretained(this)), disk_persistence_interval_); } void UploadService::PersistToDisk() { GatherHistograms(); if (current_log_) { current_log_->SaveToFile(saved_log_path_); } } void UploadService::UploadEvent() { // If the system shutdown or crashed while uploading a report, we may not have // deleted an old log. RemoveFailedLog(); if (HasStagedLog()) { // Previous upload failed, retry sending the logs. SendStagedLog(); return; } // Previous upload successful, stage another log. GatherHistograms(); StageCurrentLog(); // If a log is available for upload, upload it. if (HasStagedLog()) { SendStagedLog(); } } void UploadService::SendStagedLog() { // If metrics are not enabled, discard the log and exit. if (!AreMetricsEnabled()) { LOG(INFO) << "Metrics disabled. Don't upload metrics samples."; base::DeleteFile(staged_log_path_, false); return; } std::string staged_log; CHECK(base::ReadFileToString(staged_log_path_, &staged_log)); // Increase the failed count in case the daemon crashes while sending the log. failed_upload_count_.Add(1); if (!sender_->Send(staged_log, base::SHA1HashString(staged_log))) { LOG(WARNING) << "log failed to upload"; } else { VLOG(1) << "uploaded " << staged_log.length() << " bytes"; base::DeleteFile(staged_log_path_, false); } RemoveFailedLog(); } void UploadService::Reset() { base::DeleteFile(staged_log_path_, false); current_log_.reset(); failed_upload_count_.Set(0); } void UploadService::GatherHistograms() { base::StatisticsRecorder::Histograms histograms; base::StatisticsRecorder::GetHistograms(&histograms); histogram_snapshot_manager_.PrepareDeltas( base::Histogram::kNoFlags, base::Histogram::kUmaTargetedHistogramFlag); // Gather and reset the crash counters, shared with the binder threads. unsigned int kernel_crashes = counters_->GetAndResetKernelCrashCount(); unsigned int unclean_shutdowns = counters_->GetAndResetUncleanShutdownCount(); unsigned int user_crashes = counters_->GetAndResetUserCrashCount(); // Only create a log if the counters have changed. if (kernel_crashes > 0 || unclean_shutdowns > 0 || user_crashes > 0) { GetOrCreateCurrentLog()->IncrementKernelCrashCount(kernel_crashes); GetOrCreateCurrentLog()->IncrementUncleanShutdownCount(unclean_shutdowns); GetOrCreateCurrentLog()->IncrementUserCrashCount(user_crashes); } } void UploadService::RecordDelta(const base::HistogramBase& histogram, const base::HistogramSamples& snapshot) { GetOrCreateCurrentLog()->RecordHistogramDelta(histogram.histogram_name(), snapshot); } void UploadService::StageCurrentLog() { // If we haven't logged anything since the last upload, don't upload an empty // report. if (!current_log_) return; std::unique_ptr<MetricsLog> staged_log; staged_log.swap(current_log_); staged_log->CloseLog(); if (!staged_log->PopulateSystemProfile(system_profile_setter_.get())) { LOG(WARNING) << "Error while adding metadata to the log. Discarding the " << "log."; return; } if (!base::DeleteFile(saved_log_path_, false)) { // There is a chance that we will upload the same metrics twice but, if we // are lucky, the backup should be overridden before that. In doubt, try not // to lose any metrics. LOG(ERROR) << "failed to delete the last backup of the current log."; } failed_upload_count_.Set(0); staged_log->SaveToFile(staged_log_path_); } MetricsLog* UploadService::GetOrCreateCurrentLog() { if (!current_log_) { StartNewLog(); } return current_log_.get(); } bool UploadService::HasStagedLog() { return base::PathExists(staged_log_path_); } void UploadService::RemoveFailedLog() { if (failed_upload_count_.Get() > kMaxFailedUpload) { LOG(INFO) << "log failed more than " << kMaxFailedUpload << " times."; CHECK(base::DeleteFile(staged_log_path_, false)) << "failed to delete staged log at " << staged_log_path_.value(); failed_upload_count_.Set(0); } } bool UploadService::AreMetricsEnabled() { return base::PathExists(consent_file_); }