Merge "Allow setting an arbitrary alignment for an entry."
diff --git a/crash_reporter/README.md b/crash_reporter/README.md
new file mode 100644
index 0000000..9ac0a86
--- /dev/null
+++ b/crash_reporter/README.md
@@ -0,0 +1,61 @@
+# crash_reporter
+
+`crash_reporter` is a deamon running on the device that saves the call stack of
+crashing programs. It makes use of the
+[Breakpad](https://chromium.googlesource.com/breakpad/breakpad/) library.
+
+During a build, Breakpad symbol files are generated for all binaries. They are
+packaged into a zip file when running `m dist`, so that a developer can upload
+them to the crash server.
+
+On a device, if the user has opted in to metrics and crash reporting, a
+Breakpad minidump is generated when an executable crashes, which is then
+uploaded to the crash server.
+
+On the crash server, it compares the minidump's signature to the symbol files
+that the developer has uploaded, and extracts and symbolizes the stack trace
+from the minidump.
+
+## SELinux policies
+
+In order to correctly generate a minidump, `crash_reporter` needs to be given
+the proper SELinux permissions for accessing the domain of the crashing
+executable. By default, `crash_reporter` has only been given access to a select
+number of system domains, such as `metricsd`, `weave`, and `update_engine`. If
+a developer wants their executable's crashes to be caught by `crash_reporter`,
+they will have to set their SELinux policies in their .te file to allow
+`crash_reporter` access to their domain. This can be done through a simple
+[macro](https://android.googlesource.com/device/generic/brillo/+/master/sepolicy/te_macros):
+
+ allow_crash_reporter(domain_name)
+
+Replace *domain_name* with whatever domain is assigned to the executable in
+the `file_contexts` file.
+
+## Configuration
+
+`crash_reporter` has a few different configuration options that have to be set.
+
+- Crashes are only handled and uploaded if analytics reporting is enabled,
+ either via the weave call to set `_metrics.enableAnalyticsReporting` or by
+ manually creating the file `/data/misc/metrics/enabled` (for testing only).
+- The `BRILLO_CRASH_SERVER` make variable should be set in the `product.mk`
+ file to the URL of the crash server. For Brillo builds, it is set
+ automatically through the product configuration. Setting this variable will
+ populate the `/etc/os-release.d/crash_server` file on the device, which is
+ read by `crash_sender`.
+- The `BRILLO_PRODUCT_ID` make variable should be set in the `product.mk` file
+ to the product's ID. For Brillo builds, it is set automatically through the
+ product configuration. Setting this variable will populate the
+ `/etc/os-release.d/product_id`, which is read by `crash_sender`.
+
+## Uploading crash reports in *eng* builds
+
+By default, crash reports are only uploaded to the server for production
+*user* and *userdebug* images. In *eng* builds, with crash reporting enabled
+the device will generate minidumps for any crashing executables but will not
+send them to the crash server. If a developer does want to force an upload,
+they can do so by issuing the command `SECONDS_SEND_SPREAD=5 FORCE_OFFICIAL=1
+crash_sender` from an ADB shell. This will send the report to the server, with
+the *image_type* field set to *force-official* so that these reports can be
+differentiated from normal reports.
diff --git a/include/log/logd.h b/include/log/logd.h
index b7aedaf..b271602 100644
--- a/include/log/logd.h
+++ b/include/log/logd.h
@@ -45,6 +45,7 @@
int __android_log_bswrite(int32_t tag, const char *payload);
int __android_log_security_bwrite(int32_t tag, const void *payload, size_t len);
+int __android_log_security_bswrite(int32_t tag, const char *payload);
#ifdef __cplusplus
}
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index 5406c50..55b965b 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -668,3 +668,25 @@
return write_to_log(LOG_ID_EVENTS, vec, 4);
}
+
+/*
+ * Like __android_log_security_bwrite, but used for writing strings to the
+ * security log.
+ */
+int __android_log_security_bswrite(int32_t tag, const char *payload)
+{
+ struct iovec vec[4];
+ char type = EVENT_TYPE_STRING;
+ uint32_t len = strlen(payload);
+
+ vec[0].iov_base = &tag;
+ vec[0].iov_len = sizeof(tag);
+ vec[1].iov_base = &type;
+ vec[1].iov_len = sizeof(type);
+ vec[2].iov_base = &len;
+ vec[2].iov_len = sizeof(len);
+ vec[3].iov_base = (void*)payload;
+ vec[3].iov_len = len;
+
+ return write_to_log(LOG_ID_SECURITY, vec, 4);
+}
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
index 7381703..ed3fcbb 100644
--- a/metricsd/Android.mk
+++ b/metricsd/Android.mk
@@ -38,6 +38,7 @@
uploader/metrics_hashes.cc \
uploader/metrics_log_base.cc \
uploader/metrics_log.cc \
+ uploader/metricsd_service_runner.cc \
uploader/sender_http.cc \
uploader/system_profile_cache.cc \
uploader/upload_service.cc
@@ -84,6 +85,7 @@
metricsd_shared_libraries := \
libbinder \
libbrillo \
+ libbrillo-binder \
libbrillo-http \
libchrome \
libprotobuf-cpp-lite \
diff --git a/metricsd/constants.h b/metricsd/constants.h
index 4815888..b702737 100644
--- a/metricsd/constants.h
+++ b/metricsd/constants.h
@@ -26,6 +26,7 @@
static const char kMetricsServer[] = "https://clients4.google.com/uma/v2";
static const char kConsentFileName[] = "enabled";
static const char kStagedLogName[] = "staged_log";
+static const char kSavedLogName[] = "saved_log";
static const char kFailedUploadCountName[] = "failed_upload_count";
static const char kDefaultVersion[] = "0.0.0.0";
diff --git a/metricsd/metricsd_main.cc b/metricsd/metricsd_main.cc
index f460268..0178342 100644
--- a/metricsd/metricsd_main.cc
+++ b/metricsd/metricsd_main.cc
@@ -14,21 +14,15 @@
* limitations under the License.
*/
-#include <thread>
-
-#include <base/at_exit.h>
#include <base/command_line.h>
#include <base/files/file_path.h>
#include <base/logging.h>
-#include <base/metrics/statistics_recorder.h>
-#include <base/strings/string_util.h>
#include <base/time/time.h>
#include <brillo/flag_helper.h>
#include <brillo/syslog_logging.h>
#include "constants.h"
-#include "uploader/bn_metricsd_impl.h"
-#include "uploader/crash_counters.h"
+#include "uploader/metricsd_service_runner.h"
#include "uploader/upload_service.h"
int main(int argc, char** argv) {
@@ -39,10 +33,13 @@
// Upload Service flags.
DEFINE_int32(upload_interval_secs, 1800,
- "Interval at which metrics_daemon sends the metrics. (needs "
- "-uploader)");
+ "Interval at which metricsd uploads the metrics.");
+ DEFINE_int32(disk_persistence_interval_secs, 300,
+ "Interval at which metricsd saves the aggregated metrics to "
+ "disk to avoid losing them if metricsd stops in between "
+ "two uploads.");
DEFINE_string(server, metrics::kMetricsServer,
- "Server to upload the metrics to. (needs -uploader)");
+ "Server to upload the metrics to.");
DEFINE_string(private_directory, metrics::kMetricsdDirectory,
"Path to the private directory used by metricsd "
"(testing only)");
@@ -76,18 +73,11 @@
return errno;
}
- std::shared_ptr<CrashCounters> counters(new CrashCounters);
-
UploadService upload_service(
FLAGS_server, base::TimeDelta::FromSeconds(FLAGS_upload_interval_secs),
+ base::TimeDelta::FromSeconds(FLAGS_disk_persistence_interval_secs),
base::FilePath(FLAGS_private_directory),
- base::FilePath(FLAGS_shared_directory), counters);
+ base::FilePath(FLAGS_shared_directory));
- base::StatisticsRecorder::Initialize();
-
- // Create and start the binder thread.
- BnMetricsdImpl binder_service(counters);
- std::thread binder_thread(&BnMetricsdImpl::Run, &binder_service);
-
- upload_service.Run();
+ return upload_service.Run();
}
diff --git a/metricsd/uploader/bn_metricsd_impl.cc b/metricsd/uploader/bn_metricsd_impl.cc
index 2cbc2da..219ed60 100644
--- a/metricsd/uploader/bn_metricsd_impl.cc
+++ b/metricsd/uploader/bn_metricsd_impl.cc
@@ -19,8 +19,6 @@
#include <base/metrics/histogram.h>
#include <base/metrics/sparse_histogram.h>
#include <base/metrics/statistics_recorder.h>
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
#include <utils/Errors.h>
#include <utils/String16.h>
#include <utils/String8.h>
@@ -37,16 +35,6 @@
CHECK(counters_) << "Invalid counters argument to constructor";
}
-void BnMetricsdImpl::Run() {
- android::status_t status =
- android::defaultServiceManager()->addService(getInterfaceDescriptor(),
- this);
- CHECK(status == android::OK) << "Metricsd service registration failed";
- android::ProcessState::self()->setThreadPoolMaxThreadCount(0);
- android::IPCThreadState::self()->disableBackgroundScheduling(true);
- android::IPCThreadState::self()->joinThreadPool();
-}
-
Status BnMetricsdImpl::recordHistogram(
const String16& name, int sample, int min, int max, int nbuckets) {
base::HistogramBase* histogram = base::Histogram::FactoryGet(
diff --git a/metricsd/uploader/bn_metricsd_impl.h b/metricsd/uploader/bn_metricsd_impl.h
index 016ccb6..bf47e80 100644
--- a/metricsd/uploader/bn_metricsd_impl.h
+++ b/metricsd/uploader/bn_metricsd_impl.h
@@ -25,9 +25,6 @@
explicit BnMetricsdImpl(const std::shared_ptr<CrashCounters>& counters);
virtual ~BnMetricsdImpl() = default;
- // Starts the binder main loop.
- void Run();
-
// Records a histogram.
android::binder::Status recordHistogram(const android::String16& name,
int sample,
diff --git a/metricsd/uploader/metrics_log.cc b/metricsd/uploader/metrics_log.cc
index a01b5da..39655e6 100644
--- a/metricsd/uploader/metrics_log.cc
+++ b/metricsd/uploader/metrics_log.cc
@@ -18,6 +18,8 @@
#include <string>
+#include <base/files/file_util.h>
+
#include "uploader/proto/system_profile.pb.h"
#include "uploader/system_profile_setter.h"
@@ -27,6 +29,40 @@
: MetricsLogBase("", 0, metrics::MetricsLogBase::ONGOING_LOG, "") {
}
+bool MetricsLog::LoadFromFile(const base::FilePath& saved_log) {
+ std::string encoded_log;
+ if (!base::ReadFileToString(saved_log, &encoded_log)) {
+ LOG(ERROR) << "Failed to read the metrics log backup from "
+ << saved_log.value();
+ return false;
+ }
+
+ if (!uma_proto()->ParseFromString(encoded_log)) {
+ LOG(ERROR) << "Failed to parse log from " << saved_log.value()
+ << ", deleting the log";
+ base::DeleteFile(saved_log, false);
+ uma_proto()->Clear();
+ return false;
+ }
+
+ VLOG(1) << uma_proto()->histogram_event_size() << " histograms loaded from "
+ << saved_log.value();
+
+ return true;
+}
+
+bool MetricsLog::SaveToFile(const base::FilePath& path) {
+ std::string encoded_log;
+ GetEncodedLog(&encoded_log);
+
+ if (static_cast<int>(encoded_log.size()) !=
+ base::WriteFile(path, encoded_log.data(), encoded_log.size())) {
+ LOG(ERROR) << "Failed to persist the current log to " << path.value();
+ return false;
+ }
+ return true;
+}
+
void MetricsLog::IncrementUserCrashCount(unsigned int count) {
metrics::SystemProfileProto::Stability* stability(
uma_proto()->mutable_system_profile()->mutable_stability());
diff --git a/metricsd/uploader/metrics_log.h b/metricsd/uploader/metrics_log.h
index b76cd72..9e60b97 100644
--- a/metricsd/uploader/metrics_log.h
+++ b/metricsd/uploader/metrics_log.h
@@ -19,6 +19,7 @@
#include <string>
+#include <base/files/file_path.h>
#include <base/macros.h>
#include "uploader/metrics_log_base.h"
@@ -44,8 +45,15 @@
// Populate the system profile with system information using setter.
bool PopulateSystemProfile(SystemProfileSetter* setter);
+ // Load the log from |path|.
+ bool LoadFromFile(const base::FilePath& path);
+
+ // Save this log to |path|.
+ bool SaveToFile(const base::FilePath& path);
+
private:
friend class UploadServiceTest;
+ FRIEND_TEST(UploadServiceTest, CurrentLogSavedAndResumed);
FRIEND_TEST(UploadServiceTest, LogContainsAggregatedValues);
FRIEND_TEST(UploadServiceTest, LogContainsCrashCounts);
FRIEND_TEST(UploadServiceTest, LogKernelCrash);
diff --git a/metricsd/uploader/metricsd_service_runner.cc b/metricsd/uploader/metricsd_service_runner.cc
new file mode 100644
index 0000000..2834977
--- /dev/null
+++ b/metricsd/uploader/metricsd_service_runner.cc
@@ -0,0 +1,60 @@
+/*
+ * 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/metricsd_service_runner.h"
+
+#include <thread>
+
+#include <binder/IServiceManager.h>
+#include <brillo/binder_watcher.h>
+#include <utils/Errors.h>
+
+#include "uploader/bn_metricsd_impl.h"
+
+MetricsdServiceRunner::MetricsdServiceRunner(
+ std::shared_ptr<CrashCounters> counters)
+ : counters_(counters) {}
+
+void MetricsdServiceRunner::Start() {
+ thread_.reset(new std::thread(&MetricsdServiceRunner::Run, this));
+}
+
+void MetricsdServiceRunner::Run() {
+ android::sp<BnMetricsdImpl> metrics_service(new BnMetricsdImpl(counters_));
+
+ android::status_t status = android::defaultServiceManager()->addService(
+ metrics_service->getInterfaceDescriptor(), metrics_service);
+ CHECK(status == android::OK) << "Metricsd service registration failed";
+
+ message_loop_for_io_.reset(new base::MessageLoopForIO);
+
+ brillo::BinderWatcher watcher;
+ CHECK(watcher.Init()) << "failed to initialize the binder file descriptor "
+ << "watcher";
+
+ message_loop_for_io_->Run();
+
+ // Delete the message loop here as it needs to be deconstructed in the thread
+ // it is attached to.
+ message_loop_for_io_.reset();
+}
+
+void MetricsdServiceRunner::Stop() {
+ message_loop_for_io_->PostTask(FROM_HERE,
+ message_loop_for_io_->QuitClosure());
+
+ thread_->join();
+}
diff --git a/metricsd/uploader/metricsd_service_runner.h b/metricsd/uploader/metricsd_service_runner.h
new file mode 100644
index 0000000..1715de0
--- /dev/null
+++ b/metricsd/uploader/metricsd_service_runner.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#ifndef METRICS_UPLOADER_METRISCD_SERVICE_RUNNER_H_
+#define METRICS_UPLOADER_METRISCD_SERVICE_RUNNER_H_
+
+#include <memory>
+#include <thread>
+
+#include <base/message_loop/message_loop.h>
+
+#include "uploader/crash_counters.h"
+
+class MetricsdServiceRunner {
+ public:
+ MetricsdServiceRunner(std::shared_ptr<CrashCounters> counters);
+
+ // Start the Metricsd Binder service in a new thread.
+ void Start();
+
+ // Stop the Metricsd service and wait for its thread to exit.
+ void Stop();
+
+ private:
+ // Creates and run the main loop for metricsd's Binder service.
+ void Run();
+
+ std::unique_ptr<base::MessageLoopForIO> message_loop_for_io_;
+
+ std::unique_ptr<std::thread> thread_;
+ std::shared_ptr<CrashCounters> counters_;
+};
+
+#endif // METRICS_UPLOADER_METRISCD_SERVICE_RUNNER_H_
diff --git a/metricsd/uploader/upload_service.cc b/metricsd/uploader/upload_service.cc
index 2fb30c3..ab44b28 100644
--- a/metricsd/uploader/upload_service.cc
+++ b/metricsd/uploader/upload_service.cc
@@ -42,49 +42,88 @@
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,
- const std::shared_ptr<CrashCounters> counters)
- : histogram_snapshot_manager_(this),
+ 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_(counters),
- upload_interval_(upload_interval) {
+ 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::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();
+}
+
void UploadService::InitForTest(SystemProfileSetter* setter) {
+ LoadSavedLog();
system_profile_setter_.reset(setter);
}
void UploadService::StartNewLog() {
- CHECK(!HasStagedLog()) << "the staged log should be discarded before "
- << "starting a new metrics log";
- MetricsLog* log = new MetricsLog();
- current_log_.reset(log);
+ current_log_.reset(new MetricsLog());
}
-void UploadService::UploadEventCallback(const base::TimeDelta& interval) {
+void UploadService::UploadEventCallback() {
UploadEvent();
- base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
- base::Bind(&UploadService::UploadEventCallback,
- base::Unretained(this),
- interval),
- interval);
+ 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() {
@@ -178,14 +217,16 @@
<< "log.";
return;
}
- std::string encoded_log;
- staged_log->GetEncodedLog(&encoded_log);
+
+ 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);
- if (static_cast<int>(encoded_log.size()) != base::WriteFile(
- staged_log_path_, encoded_log.data(), encoded_log.size())) {
- LOG(ERROR) << "failed to persist to " << staged_log_path_.value();
- }
+ staged_log->SaveToFile(staged_log_path_);
}
MetricsLog* UploadService::GetOrCreateCurrentLog() {
diff --git a/metricsd/uploader/upload_service.h b/metricsd/uploader/upload_service.h
index 420653e..a1d9d3b 100644
--- a/metricsd/uploader/upload_service.h
+++ b/metricsd/uploader/upload_service.h
@@ -28,6 +28,7 @@
#include "persistent_integer.h"
#include "uploader/crash_counters.h"
#include "uploader/metrics_log.h"
+#include "uploader/metricsd_service_runner.h"
#include "uploader/proto/chrome_user_metrics_extension.pb.h"
#include "uploader/sender.h"
#include "uploader/system_profile_cache.h"
@@ -65,19 +66,22 @@
public:
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,
- const std::shared_ptr<CrashCounters> counters);
+ const base::FilePath& shared_metrics_directory);
// Initializes the upload service.
- int OnInit();
+ int OnInit() override;
+
+ // Cleans up the internal state before exiting.
+ void OnShutdown(int* exit_code) override;
// Starts a new log. The log needs to be regenerated after each successful
// launch as it is destroyed when staging the log.
void StartNewLog();
- // Event callback for handling MessageLoop events.
- void UploadEventCallback(const base::TimeDelta& interval);
+ // Saves the current metrics to a file.
+ void PersistToDisk();
// Triggers an upload event.
void UploadEvent();
@@ -97,6 +101,8 @@
friend class UploadServiceTest;
FRIEND_TEST(UploadServiceTest, CanSendMultipleTimes);
+ FRIEND_TEST(UploadServiceTest, CorruptedSavedLog);
+ FRIEND_TEST(UploadServiceTest, CurrentLogSavedAndResumed);
FRIEND_TEST(UploadServiceTest, DiscardLogsAfterTooManyFailedUpload);
FRIEND_TEST(UploadServiceTest, EmptyLogsAreNotSent);
FRIEND_TEST(UploadServiceTest, FailedSendAreRetried);
@@ -108,6 +114,7 @@
FRIEND_TEST(UploadServiceTest, LogKernelCrash);
FRIEND_TEST(UploadServiceTest, LogUncleanShutdown);
FRIEND_TEST(UploadServiceTest, LogUserCrash);
+ FRIEND_TEST(UploadServiceTest, PersistEmptyLog);
FRIEND_TEST(UploadServiceTest, UnknownCrashIgnored);
FRIEND_TEST(UploadServiceTest, ValuesInConfigFileAreSent);
@@ -118,12 +125,21 @@
// will be discarded.
static const int kMaxFailedUpload;
+ // Loads the log saved to disk if it exists.
+ void LoadSavedLog();
+
// Resets the internal state.
void Reset();
// Returns true iff metrics reporting is enabled.
bool AreMetricsEnabled();
+ // Event callback for handling Upload events.
+ void UploadEventCallback();
+
+ // Event callback for handling Persist events.
+ void PersistEventCallback();
+
// Aggregates all histogram available in memory and store them in the current
// log.
void GatherHistograms();
@@ -153,9 +169,13 @@
std::shared_ptr<CrashCounters> counters_;
base::TimeDelta upload_interval_;
+ base::TimeDelta disk_persistence_interval_;
+
+ MetricsdServiceRunner metricsd_service_runner_;
base::FilePath consent_file_;
base::FilePath staged_log_path_;
+ base::FilePath saved_log_path_;
bool testing_;
};
diff --git a/metricsd/uploader/upload_service_test.cc b/metricsd/uploader/upload_service_test.cc
index ec507e8..70112f4 100644
--- a/metricsd/uploader/upload_service_test.cc
+++ b/metricsd/uploader/upload_service_test.cc
@@ -45,18 +45,18 @@
ASSERT_FALSE(base::StatisticsRecorder::IsActive());
base::StatisticsRecorder::Initialize();
- base::FilePath private_dir = dir_.path().Append("private");
- base::FilePath shared_dir = dir_.path().Append("shared");
+ private_dir_ = dir_.path().Append("private");
+ shared_dir_ = dir_.path().Append("shared");
- EXPECT_TRUE(base::CreateDirectory(private_dir));
- EXPECT_TRUE(base::CreateDirectory(shared_dir));
+ EXPECT_TRUE(base::CreateDirectory(private_dir_));
+ EXPECT_TRUE(base::CreateDirectory(shared_dir_));
- ASSERT_EQ(0, base::WriteFile(shared_dir.Append(metrics::kConsentFileName),
+ ASSERT_EQ(0, base::WriteFile(shared_dir_.Append(metrics::kConsentFileName),
"", 0));
- counters_.reset(new CrashCounters);
- upload_service_.reset(new UploadService("", base::TimeDelta(), private_dir,
- shared_dir, counters_));
+ upload_service_.reset(new UploadService(
+ "", base::TimeDelta(), base::TimeDelta(), private_dir_, shared_dir_));
+ counters_ = upload_service_->counters_;
upload_service_->sender_.reset(new SenderMock);
upload_service_->InitForTest(new MockSystemProfileSetter);
@@ -81,15 +81,16 @@
base::FilePath filepath =
dir_.path().Append("etc/os-release.d").Append(name);
ASSERT_TRUE(base::CreateDirectory(filepath.DirName()));
- ASSERT_EQ(
- value.size(),
- base::WriteFile(filepath, value.data(), value.size()));
+ ASSERT_EQ(value.size(),
+ base::WriteFile(filepath, value.data(), value.size()));
}
const metrics::SystemProfileProto_Stability GetCurrentStability() {
EXPECT_TRUE(upload_service_->current_log_.get());
- return upload_service_->current_log_->uma_proto()->system_profile().stability();
+ return upload_service_->current_log_->uma_proto()
+ ->system_profile()
+ .stability();
}
base::ScopedTempDir dir_;
@@ -97,6 +98,8 @@
std::unique_ptr<base::AtExitManager> exit_manager_;
std::shared_ptr<CrashCounters> counters_;
+ base::FilePath private_dir_;
+ base::FilePath shared_dir_;
};
TEST_F(UploadServiceTest, FailedSendAreRetried) {
@@ -149,12 +152,9 @@
}
TEST_F(UploadServiceTest, LogEmptyByDefault) {
- UploadService upload_service("", base::TimeDelta(), dir_.path(), dir_.path(),
- std::make_shared<CrashCounters>());
-
- // current_log_ should be initialized later as it needs AtExitManager to exit
+ // current_log_ should be initialized later as it needs AtExitManager to exist
// in order to gather system information from SysInfo.
- EXPECT_FALSE(upload_service.current_log_);
+ EXPECT_FALSE(upload_service_->current_log_);
}
TEST_F(UploadServiceTest, CanSendMultipleTimes) {
@@ -222,10 +222,8 @@
}
TEST_F(UploadServiceTest, ExtractChannelFromString) {
- EXPECT_EQ(
- SystemProfileCache::ProtoChannelFromString(
- "developer-build"),
- metrics::SystemProfileProto::CHANNEL_UNKNOWN);
+ EXPECT_EQ(SystemProfileCache::ProtoChannelFromString("developer-build"),
+ metrics::SystemProfileProto::CHANNEL_UNKNOWN);
EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_DEV,
SystemProfileCache::ProtoChannelFromString("dev-channel"));
@@ -300,3 +298,38 @@
SetTestingProperty(metrics::kProductId, "hello");
ASSERT_TRUE(cache.Initialize());
}
+
+TEST_F(UploadServiceTest, CurrentLogSavedAndResumed) {
+ SendHistogram("hello", 10, 0, 100, 10);
+ upload_service_->PersistToDisk();
+ EXPECT_EQ(
+ 1, upload_service_->current_log_->uma_proto()->histogram_event().size());
+ upload_service_.reset(new UploadService(
+ "", base::TimeDelta(), base::TimeDelta(), private_dir_, shared_dir_));
+ upload_service_->InitForTest(nullptr);
+
+ SendHistogram("hello", 10, 0, 100, 10);
+ upload_service_->GatherHistograms();
+ EXPECT_EQ(2, upload_service_->GetOrCreateCurrentLog()
+ ->uma_proto()
+ ->histogram_event()
+ .size());
+}
+
+TEST_F(UploadServiceTest, PersistEmptyLog) {
+ upload_service_->PersistToDisk();
+ EXPECT_FALSE(base::PathExists(upload_service_->saved_log_path_));
+}
+
+TEST_F(UploadServiceTest, CorruptedSavedLog) {
+ // Write a bogus saved log.
+ EXPECT_EQ(5, base::WriteFile(upload_service_->saved_log_path_, "hello", 5));
+
+ upload_service_.reset(new UploadService(
+ "", base::TimeDelta(), base::TimeDelta(), private_dir_, shared_dir_));
+
+ upload_service_->InitForTest(nullptr);
+ // If the log is unreadable, we drop it and continue execution.
+ ASSERT_NE(nullptr, upload_service_->GetOrCreateCurrentLog());
+ ASSERT_FALSE(base::PathExists(upload_service_->saved_log_path_));
+}