upload_service.cc revision 6b8629a6490d01196368ae1ed5bc6967c6f127eb
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 <string>
22
23#include <base/bind.h>
24#include <base/files/file_util.h>
25#include <base/logging.h>
26#include <base/memory/scoped_vector.h>
27#include <base/message_loop/message_loop.h>
28#include <base/metrics/histogram.h>
29#include <base/metrics/histogram_base.h>
30#include <base/metrics/histogram_snapshot_manager.h>
31#include <base/metrics/sparse_histogram.h>
32#include <base/metrics/statistics_recorder.h>
33#include <base/sha1.h>
34
35#include "constants.h"
36#include "uploader/metrics_log.h"
37#include "uploader/sender_http.h"
38#include "uploader/system_profile_setter.h"
39
40const int UploadService::kMaxFailedUpload = 10;
41
42UploadService::UploadService(const std::string& server,
43                             const base::TimeDelta& upload_interval,
44                             const base::FilePath& private_metrics_directory,
45                             const base::FilePath& shared_metrics_directory,
46                             const std::shared_ptr<CrashCounters> counters)
47    : histogram_snapshot_manager_(this),
48      sender_(new HttpSender(server)),
49      failed_upload_count_(metrics::kFailedUploadCountName,
50                           private_metrics_directory),
51      counters_(counters),
52      upload_interval_(upload_interval) {
53  staged_log_path_ = private_metrics_directory.Append(metrics::kStagedLogName);
54  consent_file_ = shared_metrics_directory.Append(metrics::kConsentFileName);
55}
56
57int UploadService::OnInit() {
58  system_profile_setter_.reset(new SystemProfileCache());
59
60  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
61      base::Bind(&UploadService::UploadEventCallback,
62                 base::Unretained(this),
63                 upload_interval_),
64      upload_interval_);
65  return EX_OK;
66}
67
68void UploadService::InitForTest(SystemProfileSetter* setter) {
69  system_profile_setter_.reset(setter);
70}
71
72void UploadService::StartNewLog() {
73  CHECK(!HasStagedLog()) << "the staged log should be discarded before "
74                         << "starting a new metrics log";
75  MetricsLog* log = new MetricsLog();
76  current_log_.reset(log);
77}
78
79void UploadService::UploadEventCallback(const base::TimeDelta& interval) {
80  UploadEvent();
81
82  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
83      base::Bind(&UploadService::UploadEventCallback,
84                 base::Unretained(this),
85                 interval),
86      interval);
87}
88
89void UploadService::UploadEvent() {
90  // If the system shutdown or crashed while uploading a report, we may not have
91  // deleted an old log.
92  RemoveFailedLog();
93
94  if (HasStagedLog()) {
95    // Previous upload failed, retry sending the logs.
96    SendStagedLog();
97    return;
98  }
99
100  // Previous upload successful, stage another log.
101  GatherHistograms();
102  StageCurrentLog();
103
104  // If a log is available for upload, upload it.
105  if (HasStagedLog()) {
106    SendStagedLog();
107  }
108}
109
110void UploadService::SendStagedLog() {
111  // If metrics are not enabled, discard the log and exit.
112  if (!AreMetricsEnabled()) {
113    LOG(INFO) << "Metrics disabled. Don't upload metrics samples.";
114    base::DeleteFile(staged_log_path_, false);
115    return;
116  }
117
118  std::string staged_log;
119  CHECK(base::ReadFileToString(staged_log_path_, &staged_log));
120
121  // Increase the failed count in case the daemon crashes while sending the log.
122  failed_upload_count_.Add(1);
123
124  if (!sender_->Send(staged_log, base::SHA1HashString(staged_log))) {
125    LOG(WARNING) << "log failed to upload";
126  } else {
127    VLOG(1) << "uploaded " << staged_log.length() << " bytes";
128    base::DeleteFile(staged_log_path_, false);
129  }
130
131  RemoveFailedLog();
132}
133
134void UploadService::Reset() {
135  base::DeleteFile(staged_log_path_, false);
136  current_log_.reset();
137  failed_upload_count_.Set(0);
138}
139
140void UploadService::GatherHistograms() {
141  base::StatisticsRecorder::Histograms histograms;
142  base::StatisticsRecorder::GetHistograms(&histograms);
143
144  histogram_snapshot_manager_.PrepareDeltas(
145      base::Histogram::kNoFlags, base::Histogram::kUmaTargetedHistogramFlag);
146
147  // Gather and reset the crash counters, shared with the binder threads.
148  unsigned int kernel_crashes = counters_->GetAndResetKernelCrashCount();
149  unsigned int unclean_shutdowns = counters_->GetAndResetUncleanShutdownCount();
150  unsigned int user_crashes = counters_->GetAndResetUserCrashCount();
151
152  // Only create a log if the counters have changed.
153  if (kernel_crashes > 0 || unclean_shutdowns > 0 || user_crashes > 0) {
154    GetOrCreateCurrentLog()->IncrementKernelCrashCount(kernel_crashes);
155    GetOrCreateCurrentLog()->IncrementUncleanShutdownCount(unclean_shutdowns);
156    GetOrCreateCurrentLog()->IncrementUserCrashCount(user_crashes);
157  }
158}
159
160void UploadService::RecordDelta(const base::HistogramBase& histogram,
161                                const base::HistogramSamples& snapshot) {
162  GetOrCreateCurrentLog()->RecordHistogramDelta(histogram.histogram_name(),
163                                                snapshot);
164}
165
166void UploadService::StageCurrentLog() {
167  // If we haven't logged anything since the last upload, don't upload an empty
168  // report.
169  if (!current_log_)
170    return;
171
172  scoped_ptr<MetricsLog> staged_log;
173  staged_log.swap(current_log_);
174  staged_log->CloseLog();
175  if (!staged_log->PopulateSystemProfile(system_profile_setter_.get())) {
176    LOG(WARNING) << "Error while adding metadata to the log. Discarding the "
177                 << "log.";
178    return;
179  }
180  std::string encoded_log;
181  staged_log->GetEncodedLog(&encoded_log);
182
183  failed_upload_count_.Set(0);
184  if (static_cast<int>(encoded_log.size()) != base::WriteFile(
185      staged_log_path_, encoded_log.data(), encoded_log.size())) {
186    LOG(ERROR) << "failed to persist to " << staged_log_path_.value();
187  }
188}
189
190MetricsLog* UploadService::GetOrCreateCurrentLog() {
191  if (!current_log_) {
192    StartNewLog();
193  }
194  return current_log_.get();
195}
196
197bool UploadService::HasStagedLog() {
198  return base::PathExists(staged_log_path_);
199}
200
201void UploadService::RemoveFailedLog() {
202  if (failed_upload_count_.Get() > kMaxFailedUpload) {
203    LOG(INFO) << "log failed more than " << kMaxFailedUpload << " times.";
204    CHECK(base::DeleteFile(staged_log_path_, false))
205        << "failed to delete staged log at " << staged_log_path_.value();
206    failed_upload_count_.Set(0);
207  }
208}
209
210bool UploadService::AreMetricsEnabled() {
211  return base::PathExists(consent_file_);
212}
213