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