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 
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)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 
LoadSavedLog()62 void UploadService::LoadSavedLog() {
63   if (base::PathExists(saved_log_path_)) {
64     GetOrCreateCurrentLog()->LoadFromFile(saved_log_path_);
65   }
66 }
67 
OnInit()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 
OnShutdown(int * exit_code)91 void UploadService::OnShutdown(int* exit_code) {
92   metricsd_service_runner_.Stop();
93   PersistToDisk();
94 }
95 
InitForTest(SystemProfileSetter * setter)96 void UploadService::InitForTest(SystemProfileSetter* setter) {
97   LoadSavedLog();
98   system_profile_setter_.reset(setter);
99 }
100 
StartNewLog()101 void UploadService::StartNewLog() {
102   current_log_.reset(new MetricsLog());
103 }
104 
UploadEventCallback()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 
PersistEventCallback()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 
PersistToDisk()123 void UploadService::PersistToDisk() {
124   GatherHistograms();
125   if (current_log_) {
126     current_log_->SaveToFile(saved_log_path_);
127   }
128 }
129 
UploadEvent()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 
SendStagedLog()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 
Reset()175 void UploadService::Reset() {
176   base::DeleteFile(staged_log_path_, false);
177   current_log_.reset();
178   failed_upload_count_.Set(0);
179 }
180 
GatherHistograms()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 
RecordDelta(const base::HistogramBase & histogram,const base::HistogramSamples & snapshot)201 void UploadService::RecordDelta(const base::HistogramBase& histogram,
202                                 const base::HistogramSamples& snapshot) {
203   GetOrCreateCurrentLog()->RecordHistogramDelta(histogram.histogram_name(),
204                                                 snapshot);
205 }
206 
StageCurrentLog()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 
GetOrCreateCurrentLog()233 MetricsLog* UploadService::GetOrCreateCurrentLog() {
234   if (!current_log_) {
235     StartNewLog();
236   }
237   return current_log_.get();
238 }
239 
HasStagedLog()240 bool UploadService::HasStagedLog() {
241   return base::PathExists(staged_log_path_);
242 }
243 
RemoveFailedLog()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 
AreMetricsEnabled()253 bool UploadService::AreMetricsEnabled() {
254   return base::PathExists(consent_file_);
255 }
256