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