Merge "Remove no-longer-needed crypto init builtins"
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 25e8376..386f221 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -306,12 +306,14 @@
auto it = g_poll_node_map.find(subproc_fd);
if (it == g_poll_node_map.end()) {
D("subproc_fd %d cleared from fd_table", subproc_fd);
+ adb_close(subproc_fd);
return;
}
fdevent* subproc_fde = it->second.fde;
if(subproc_fde->fd != subproc_fd) {
// Already reallocated?
- D("subproc_fd(%d) != subproc_fde->fd(%d)", subproc_fd, subproc_fde->fd);
+ LOG(FATAL) << "subproc_fd(" << subproc_fd << ") != subproc_fde->fd(" << subproc_fde->fd
+ << ")";
return;
}
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index 366ed07..e092dc4 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -183,7 +183,6 @@
~Subprocess();
const std::string& command() const { return command_; }
- bool is_interactive() const { return command_.empty(); }
int local_socket_fd() const { return local_socket_sfd_.fd(); }
@@ -233,6 +232,7 @@
}
Subprocess::~Subprocess() {
+ WaitForExit();
}
bool Subprocess::ForkAndExec() {
@@ -331,7 +331,7 @@
parent_error_sfd.Reset();
close_on_exec(child_error_sfd.fd());
- if (is_interactive()) {
+ if (command_.empty()) {
execle(_PATH_BSHELL, _PATH_BSHELL, "-", nullptr, cenv.data());
} else {
execle(_PATH_BSHELL, _PATH_BSHELL, "-c", command_.c_str(), nullptr, cenv.data());
@@ -408,20 +408,6 @@
exit(-1);
}
- if (!is_interactive()) {
- termios tattr;
- if (tcgetattr(child_fd, &tattr) == -1) {
- WriteFdExactly(error_sfd->fd(), "tcgetattr failed");
- exit(-1);
- }
-
- cfmakeraw(&tattr);
- if (tcsetattr(child_fd, TCSADRAIN, &tattr) == -1) {
- WriteFdExactly(error_sfd->fd(), "tcsetattr failed");
- exit(-1);
- }
- }
-
return child_fd;
}
@@ -432,7 +418,6 @@
"shell srvc %d", subprocess->local_socket_fd()));
subprocess->PassDataStreams();
- subprocess->WaitForExit();
D("deleting Subprocess for PID %d", subprocess->pid());
delete subprocess;
diff --git a/bootstat/Android.mk b/bootstat/Android.mk
new file mode 100644
index 0000000..348db88
--- /dev/null
+++ b/bootstat/Android.mk
@@ -0,0 +1,137 @@
+#
+# Copyright (C) 2016 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+bootstat_c_includes := external/gtest/include
+
+bootstat_lib_src_files := \
+ boot_event_record_store.cpp \
+ event_log_list_builder.cpp
+
+bootstat_src_files := \
+ bootstat.cpp
+
+bootstat_test_src_files := \
+ boot_event_record_store_test.cpp \
+ event_log_list_builder_test.cpp \
+ testrunner.cpp
+
+bootstat_shared_libs := \
+ libbase \
+ liblog
+
+bootstat_cflags := \
+ -Wall \
+ -Wextra \
+ -Werror
+
+bootstat_cppflags := \
+ -Wno-non-virtual-dtor
+
+bootstat_debug_cflags := \
+ $(bootstat_cflags) \
+ -UNDEBUG
+
+# 524291 corresponds to sysui_histogram, from
+# frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags
+bootstat_cflags += -DHISTOGRAM_LOG_TAG=524291
+
+
+# bootstat static library
+# -----------------------------------------------------------------------------
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libbootstat
+LOCAL_CFLAGS := $(bootstat_cflags)
+LOCAL_CPPFLAGS := $(bootstat_cppflags)
+LOCAL_C_INCLUDES := $(bootstat_c_includes)
+LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
+LOCAL_SRC_FILES := $(bootstat_lib_src_files)
+
+include $(BUILD_STATIC_LIBRARY)
+
+# bootstat static library, debug
+# -----------------------------------------------------------------------------
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libbootstat_debug
+LOCAL_CFLAGS := $(bootstat_cflags)
+LOCAL_CPPFLAGS := $(bootstat_debug_cppflags)
+LOCAL_C_INCLUDES := $(bootstat_c_includes)
+LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
+LOCAL_SRC_FILES := $(bootstat_lib_src_files)
+
+include $(BUILD_STATIC_LIBRARY)
+
+# bootstat host static library, debug
+# -----------------------------------------------------------------------------
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libbootstat_host_debug
+LOCAL_CFLAGS := $(bootstat_debug_cflags)
+LOCAL_CPPFLAGS := $(bootstat_cppflags)
+LOCAL_C_INCLUDES := $(bootstat_c_includes)
+LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
+LOCAL_SRC_FILES := $(bootstat_lib_src_files)
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+# bootstat binary
+# -----------------------------------------------------------------------------
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := bootstat
+LOCAL_CFLAGS := $(bootstat_cflags)
+LOCAL_CPPFLAGS := $(bootstat_cppflags)
+LOCAL_C_INCLUDES := $(bootstat_c_includes)
+LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
+LOCAL_STATIC_LIBRARIES := libbootstat
+LOCAL_SRC_FILES := $(bootstat_src_files)
+
+include $(BUILD_EXECUTABLE)
+
+# Native tests
+# -----------------------------------------------------------------------------
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := bootstat_tests
+LOCAL_CFLAGS := $(bootstat_tests_cflags)
+LOCAL_CPPFLAGS := $(bootstat_cppflags)
+LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
+LOCAL_STATIC_LIBRARIES := libbootstat_debug libgmock
+LOCAL_SRC_FILES := $(bootstat_test_src_files)
+
+include $(BUILD_NATIVE_TEST)
+
+# Host native tests
+# -----------------------------------------------------------------------------
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := bootstat_tests
+LOCAL_CFLAGS := $(bootstat_tests_cflags)
+LOCAL_CPPFLAGS := $(bootstat_cppflags)
+LOCAL_SHARED_LIBRARIES := $(bootstat_shared_libs)
+LOCAL_STATIC_LIBRARIES := libbootstat_host_debug libgmock_host
+LOCAL_SRC_FILES := $(bootstat_test_src_files)
+
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/bootstat/README.md b/bootstat/README.md
new file mode 100644
index 0000000..1b4bf7f
--- /dev/null
+++ b/bootstat/README.md
@@ -0,0 +1,48 @@
+# bootstat #
+
+The bootstat command records boot events (e.g., `firmware_loaded`,
+`boot_complete`) and the relative time at which these events occurred. The
+command also aggregates boot event metrics locally and logs the metrics for
+analysis.
+
+ Usage: bootstat [options]
+ options include:
+ -d Dump the boot event records to the console.
+ -h Show this help.
+ -l Log all metrics to logstorage.
+ -r Record the relative time of a named boot event.
+
+## Relative time ##
+
+The timestamp recorded by bootstat is the uptime of the system, i.e., the
+number of seconds since the system booted.
+
+## Recording boot events ##
+
+To record the relative time of an event during the boot phase, call `bootstat`
+with the `-r` option and the name of the boot event.
+
+ $ bootstat -r boot_complete
+
+The relative time at which the command runs is recorded along with the name of
+the boot event to be persisted.
+
+## Logging boot events ##
+
+To log the persisted boot events, call `bootstat` with the `-l` option.
+
+ $ bootstat -l
+
+bootstat logs all boot events recorded using the `-r` option to the EventLog
+using the Tron histogram. These logs may be uploaded by interested parties
+for aggregation and analysis of boot time across different devices and
+versions.
+
+## Printing boot events ##
+
+To print the set of persisted boot events, call `bootstat` with the `-p` option.
+
+ $ bootstat -p
+ Boot events:
+ ------------
+ boot_complete 71
\ No newline at end of file
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
new file mode 100644
index 0000000..4dab92e
--- /dev/null
+++ b/bootstat/boot_event_record_store.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2016 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 "boot_event_record_store.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <utime.h>
+#include <cstdlib>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+namespace {
+
+const char BOOTSTAT_DATA_DIR[] = "/data/misc/bootstat/";
+
+// Given a boot even record file at |path|, extracts the event's relative time
+// from the record into |uptime|.
+bool ParseRecordEventTime(const std::string& path, int32_t* uptime) {
+ DCHECK_NE(static_cast<int32_t*>(nullptr), uptime);
+
+ struct stat file_stat;
+ if (stat(path.c_str(), &file_stat) == -1) {
+ PLOG(ERROR) << "Failed to read " << path;
+ return false;
+ }
+
+ *uptime = file_stat.st_mtime;
+ return true;
+}
+
+} // namespace
+
+BootEventRecordStore::BootEventRecordStore() {
+ SetStorePath(BOOTSTAT_DATA_DIR);
+}
+
+void BootEventRecordStore::AddBootEvent(const std::string& name) {
+ std::string uptime_str;
+ if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
+ LOG(ERROR) << "Failed to read /proc/uptime";
+ }
+
+ std::string record_path = GetBootEventPath(name);
+ if (creat(record_path.c_str(), S_IRUSR | S_IWUSR) == -1) {
+ PLOG(ERROR) << "Failed to create " << record_path;
+ }
+
+ struct stat file_stat;
+ if (stat(record_path.c_str(), &file_stat) == -1) {
+ PLOG(ERROR) << "Failed to read " << record_path;
+ }
+
+ // Cast intentionally rounds down.
+ time_t uptime = static_cast<time_t>(strtod(uptime_str.c_str(), NULL));
+ struct utimbuf times = {file_stat.st_atime, uptime};
+ if (utime(record_path.c_str(), ×) == -1) {
+ PLOG(ERROR) << "Failed to set mtime for " << record_path;
+ }
+}
+
+std::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore::
+ GetAllBootEvents() const {
+ std::vector<BootEventRecord> events;
+
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(store_path_.c_str()), closedir);
+
+ // This case could happen due to external manipulation of the filesystem,
+ // so crash out if the record store doesn't exist.
+ CHECK_NE(static_cast<DIR*>(nullptr), dir.get());
+
+ struct dirent* entry;
+ while ((entry = readdir(dir.get())) != NULL) {
+ // Only parse regular files.
+ if (entry->d_type != DT_REG) {
+ continue;
+ }
+
+ const std::string event = entry->d_name;
+ const std::string record_path = GetBootEventPath(event);
+ int32_t uptime;
+ if (!ParseRecordEventTime(record_path, &uptime)) {
+ LOG(ERROR) << "Failed to parse boot time record: " << record_path;
+ continue;
+ }
+
+ events.push_back(std::make_pair(event, uptime));
+ }
+
+ return events;
+}
+
+void BootEventRecordStore::SetStorePath(const std::string& path) {
+ DCHECK_EQ('/', path.back());
+ store_path_ = path;
+}
+
+std::string BootEventRecordStore::GetBootEventPath(
+ const std::string& event) const {
+ DCHECK_EQ('/', store_path_.back());
+ return store_path_ + event;
+}
diff --git a/bootstat/boot_event_record_store.h b/bootstat/boot_event_record_store.h
new file mode 100644
index 0000000..77978ef
--- /dev/null
+++ b/bootstat/boot_event_record_store.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 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 BOOT_EVENT_RECORD_STORE_H_
+#define BOOT_EVENT_RECORD_STORE_H_
+
+#include <cstdint>
+#include <string>
+#include <utility>
+#include <vector>
+#include <android-base/macros.h>
+#include <gtest/gtest_prod.h>
+
+// BootEventRecordStore manages the persistence of boot events to the record
+// store and the retrieval of all boot event records from the store.
+class BootEventRecordStore {
+ public:
+ // A BootEventRecord consists of the event name and the timestamp the event
+ // occurred.
+ typedef std::pair<std::string, int32_t> BootEventRecord;
+
+ BootEventRecordStore();
+
+ // Persists the boot event named |name| in the record store.
+ void AddBootEvent(const std::string& name);
+
+ // Returns a list of all of the boot events persisted in the record store.
+ std::vector<BootEventRecord> GetAllBootEvents() const;
+
+ private:
+ // The tests call SetStorePath to override the default store location with a
+ // more test-friendly path.
+ FRIEND_TEST(BootEventRecordStoreTest, AddSingleBootEvent);
+ FRIEND_TEST(BootEventRecordStoreTest, AddMultipleBootEvents);
+
+ // Sets the filesystem path of the record store.
+ void SetStorePath(const std::string& path);
+
+ // Constructs the full path of the given boot |event|.
+ std::string GetBootEventPath(const std::string& event) const;
+
+ // The filesystem path of the record store.
+ std::string store_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(BootEventRecordStore);
+};
+
+#endif // BOOT_EVENT_RECORD_STORE_H_
\ No newline at end of file
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
new file mode 100644
index 0000000..384f84d
--- /dev/null
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2016 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 "boot_event_record_store.h"
+
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <cstdint>
+#include <cstdlib>
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+using testing::UnorderedElementsAreArray;
+
+namespace {
+
+// Returns true if the time difference between |a| and |b| is no larger
+// than 10 seconds. This allow for a relatively large fuzz when comparing
+// two timestamps taken back-to-back.
+bool FuzzUptimeEquals(int32_t a, int32_t b) {
+ const int32_t FUZZ_SECONDS = 10;
+ return (abs(a - b) <= FUZZ_SECONDS);
+}
+
+// Returns the uptime as read from /proc/uptime, rounded down to an integer.
+int32_t ReadUptime() {
+ std::string uptime_str;
+ if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
+ return -1;
+ }
+
+ // Cast to int to round down.
+ return static_cast<int32_t>(strtod(uptime_str.c_str(), NULL));
+}
+
+// Recursively deletes the directory at |path|.
+void DeleteDirectory(const std::string& path) {
+ typedef std::unique_ptr<DIR, decltype(&closedir)> ScopedDIR;
+ ScopedDIR dir(opendir(path.c_str()), closedir);
+ ASSERT_NE(nullptr, dir.get());
+
+ struct dirent* entry;
+ while ((entry = readdir(dir.get())) != NULL) {
+ const std::string entry_name(entry->d_name);
+ if (entry_name == "." || entry_name == "..") {
+ continue;
+ }
+
+ const std::string entry_path = path + "/" + entry_name;
+ if (entry->d_type == DT_DIR) {
+ DeleteDirectory(entry_path);
+ } else {
+ unlink(entry_path.c_str());
+ }
+ }
+
+ rmdir(path.c_str());
+}
+
+class BootEventRecordStoreTest : public ::testing::Test {
+ public:
+ BootEventRecordStoreTest() {
+ store_path_ = std::string(store_dir_.path) + "/";
+ }
+
+ const std::string& GetStorePathForTesting() const {
+ return store_path_;
+ }
+
+ private:
+ void TearDown() {
+ // This removes the record store temporary directory even though
+ // TemporaryDir should already take care of it, but this method cleans up
+ // the test files added to the directory which prevent TemporaryDir from
+ // being able to remove the directory.
+ DeleteDirectory(store_path_);
+ }
+
+ // A scoped temporary directory. Using this abstraction provides creation of
+ // the directory and the path to the directory, which is stored in
+ // |store_path_|.
+ TemporaryDir store_dir_;
+
+ // The path to the temporary directory used by the BootEventRecordStore to
+ // persist records. The directory is created and destroyed for each test.
+ std::string store_path_;
+
+ DISALLOW_COPY_AND_ASSIGN(BootEventRecordStoreTest);
+};
+
+} // namespace
+
+TEST_F(BootEventRecordStoreTest, AddSingleBootEvent) {
+ BootEventRecordStore store;
+ store.SetStorePath(GetStorePathForTesting());
+
+ int32_t uptime = ReadUptime();
+ ASSERT_NE(-1, uptime);
+
+ store.AddBootEvent("cenozoic");
+
+ auto events = store.GetAllBootEvents();
+ ASSERT_EQ(1U, events.size());
+ EXPECT_EQ("cenozoic", events[0].first);
+ EXPECT_TRUE(FuzzUptimeEquals(uptime, events[0].second));
+}
+
+TEST_F(BootEventRecordStoreTest, AddMultipleBootEvents) {
+ BootEventRecordStore store;
+ store.SetStorePath(GetStorePathForTesting());
+
+ int32_t uptime = ReadUptime();
+ ASSERT_NE(-1, uptime);
+
+ store.AddBootEvent("cretaceous");
+ store.AddBootEvent("jurassic");
+ store.AddBootEvent("triassic");
+
+ const std::string EXPECTED_NAMES[] = {
+ "cretaceous",
+ "jurassic",
+ "triassic",
+ };
+
+ auto events = store.GetAllBootEvents();
+ ASSERT_EQ(3U, events.size());
+
+ std::vector<std::string> names;
+ std::vector<int32_t> timestamps;
+ for (auto i = events.begin(); i != events.end(); ++i) {
+ names.push_back(i->first);
+ timestamps.push_back(i->second);
+ }
+
+ EXPECT_THAT(names, UnorderedElementsAreArray(EXPECTED_NAMES));
+
+ for (auto i = timestamps.cbegin(); i != timestamps.cend(); ++i) {
+ EXPECT_TRUE(FuzzUptimeEquals(uptime, *i));
+ }
+}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
new file mode 100644
index 0000000..a83f8ad
--- /dev/null
+++ b/bootstat/bootstat.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+// The bootstat command provides options to persist boot events with the current
+// timestamp, dump the persisted events, and log all events to EventLog to be
+// uploaded to Android log storage via Tron.
+
+//#define LOG_TAG "bootstat"
+
+#include <unistd.h>
+#include <cstddef>
+#include <cstdio>
+#include <memory>
+#include <string>
+#include <android-base/logging.h>
+#include <log/log.h>
+#include "boot_event_record_store.h"
+#include "event_log_list_builder.h"
+
+namespace {
+
+// Builds an EventLog buffer named |event| containing |data| and writes
+// the log into the Tron histogram logs.
+void LogBootEvent(const std::string& event, int32_t data) {
+ LOG(INFO) << "Logging boot time: " << event << " " << data;
+
+ EventLogListBuilder log_builder;
+ log_builder.Append(event);
+ log_builder.Append(data);
+
+ std::unique_ptr<uint8_t[]> log;
+ size_t size;
+ log_builder.Release(&log, &size);
+
+ android_bWriteLog(HISTOGRAM_LOG_TAG, log.get(), size);
+}
+
+// Scans the boot event record store for record files and logs each boot event
+// via EventLog.
+void LogBootEvents() {
+ BootEventRecordStore boot_event_store;
+
+ auto events = boot_event_store.GetAllBootEvents();
+ for (auto i = events.cbegin(); i != events.cend(); ++i) {
+ LogBootEvent(i->first, i->second);
+ }
+}
+
+void PrintBootEvents() {
+ printf("Boot events:\n");
+ printf("------------\n");
+
+ BootEventRecordStore boot_event_store;
+ auto events = boot_event_store.GetAllBootEvents();
+ for (auto i = events.cbegin(); i != events.cend(); ++i) {
+ printf("%s\t%d\n", i->first.c_str(), i->second);
+ }
+}
+
+void ShowHelp(const char *cmd) {
+ fprintf(stderr, "Usage: %s [options]\n", cmd);
+ fprintf(stderr,
+ "options include:\n"
+ " -d Dump the boot event records to the console.\n"
+ " -h Show this help.\n"
+ " -l Log all metrics to logstorage.\n"
+ " -r Record the timestamp of a named boot event.\n");
+}
+
+// Constructs a readable, printable string from the givencommand line
+// arguments.
+std::string GetCommandLine(int argc, char **argv) {
+ std::string cmd;
+ for (int i = 0; i < argc; ++i) {
+ cmd += argv[i];
+ cmd += " ";
+ }
+
+ return cmd;
+}
+
+} // namespace
+
+int main(int argc, char **argv) {
+ android::base::InitLogging(argv);
+
+ const std::string cmd_line = GetCommandLine(argc, argv);
+ LOG(INFO) << "Service started: " << cmd_line;
+
+ int opt = 0;
+ while ((opt = getopt(argc, argv, "hlpr:")) != -1) {
+ switch (opt) {
+ case 'h': {
+ ShowHelp(argv[0]);
+ break;
+ }
+
+ case 'l': {
+ LogBootEvents();
+ break;
+ }
+
+ case 'p': {
+ PrintBootEvents();
+ break;
+ }
+
+ case 'r': {
+ // |optarg| is an external variable set by getopt representing
+ // the option argument.
+ const char* event = optarg;
+
+ BootEventRecordStore boot_event_store;
+ boot_event_store.AddBootEvent(event);
+ break;
+ }
+
+ default: {
+ DCHECK_EQ(opt, '?');
+
+ // |optopt| is an external variable set by getopt representing
+ // the value of the invalid option.
+ LOG(ERROR) << "Invalid option: " << optopt;
+ ShowHelp(argv[0]);
+ return EXIT_FAILURE;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/bootstat/event_log_list_builder.cpp b/bootstat/event_log_list_builder.cpp
new file mode 100644
index 0000000..7eb355a
--- /dev/null
+++ b/bootstat/event_log_list_builder.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2016 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 "event_log_list_builder.h"
+
+#include <cinttypes>
+#include <string>
+#include <android-base/logging.h>
+#include <log/log.h>
+
+namespace {
+
+const size_t MAX_EVENT_PAYLOAD_SIZE = 512 - 1; // Leave room for final '\n'.
+const size_t EVENT_TYPE_SIZE = 1; // Size in bytes of the event type marker.
+
+} // namespace
+
+EventLogListBuilder::EventLogListBuilder()
+ : payload_count_(0),
+ payload_size_(0),
+ payload_(std::make_unique<uint8_t[]>(MAX_EVENT_PAYLOAD_SIZE)) {
+ memset(payload_.get(), 0, MAX_EVENT_PAYLOAD_SIZE);
+
+ // Set up the top-level EventLog data type.
+ AppendByte(EVENT_TYPE_LIST);
+
+ // Skip over the byte prepresenting the number of items in the list. This
+ // value is set in Release().
+ payload_size_++;
+}
+
+bool EventLogListBuilder::Append(int value) {
+ DCHECK_NE(static_cast<uint8_t*>(nullptr), payload_.get());
+
+ if (!IsSpaceAvailable(sizeof(value) + EVENT_TYPE_SIZE)) {
+ return false;
+ }
+
+ AppendByte(EVENT_TYPE_INT);
+ AppendData(&value, sizeof(value));
+
+ payload_count_++;
+ return true;
+}
+
+bool EventLogListBuilder::Append(const std::string& value) {
+ DCHECK_NE(static_cast<uint8_t*>(nullptr), payload_.get());
+
+ int len = value.length();
+ if (!IsSpaceAvailable(sizeof(len) + len)) {
+ return false;
+ }
+
+ AppendByte(EVENT_TYPE_STRING);
+ AppendData(&len, sizeof(len));
+ AppendData(value.c_str(), len);
+
+ payload_count_++;
+ return true;
+}
+
+void EventLogListBuilder::Release(std::unique_ptr<uint8_t[]>* log,
+ size_t* size) {
+ // Finalize the log payload.
+ payload_[1] = payload_count_;
+
+ // Return the log payload.
+ *size = payload_size_;
+ *log = std::move(payload_);
+}
+
+void EventLogListBuilder::AppendData(const void* data, size_t size) {
+ DCHECK_LT(payload_size_ + size, MAX_EVENT_PAYLOAD_SIZE);
+ memcpy(&payload_[payload_size_], data, size);
+ payload_size_ += size;
+}
+
+void EventLogListBuilder::AppendByte(uint8_t byte) {
+ DCHECK_LT(payload_size_ + sizeof(byte), MAX_EVENT_PAYLOAD_SIZE);
+ payload_[payload_size_++] = byte;
+}
+
+bool EventLogListBuilder::IsSpaceAvailable(size_t value_size) {
+ size_t space_needed = value_size + EVENT_TYPE_SIZE;
+ if (payload_size_ + space_needed > MAX_EVENT_PAYLOAD_SIZE) {
+ size_t remaining = MAX_EVENT_PAYLOAD_SIZE - payload_size_;
+ LOG(WARNING) << "Not enough space for value. remain=" <<
+ remaining << "; needed=" << space_needed;
+ return false;
+ }
+
+ return true;
+}
diff --git a/bootstat/event_log_list_builder.h b/bootstat/event_log_list_builder.h
new file mode 100644
index 0000000..4e29b01
--- /dev/null
+++ b/bootstat/event_log_list_builder.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 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 EVENT_LOG_LIST_BUILDER_H_
+#define EVENT_LOG_LIST_BUILDER_H_
+
+#include <cstdint>
+#include <memory>
+
+#include <android-base/macros.h>
+
+// EventLogListBuilder provides a mechanism to build an EventLog list
+// consisting of int and string EventLog values.
+//
+// NOTE: This class does not provide the ability to append an embedded list,
+// i.e., a list containing a list.
+class EventLogListBuilder {
+ public:
+ EventLogListBuilder();
+
+ // Append a single value of a specified type.
+ bool Append(int value);
+ bool Append(const std::string& value);
+
+ // Finalizes construction of the EventLog list and releases the data
+ // to the caller. Caller takes ownership of the payload. No further calls
+ // to append* may be made once the payload is acquired by the caller.
+ void Release(std::unique_ptr<uint8_t[]>* log, size_t* size);
+
+ private:
+ // Appends |data| of the given |size| to the payload.
+ void AppendData(const void* data, size_t size);
+
+ // Appends a single byte to the payload.
+ void AppendByte(uint8_t byte);
+
+ // Returns true iff the remaining capacity in |payload_| is large enough to
+ // accommodate |value_size| bytes. The space required to log the event type
+ // is included in the internal calculation so must not be passed in to
+ // |value_size|.
+ bool IsSpaceAvailable(size_t value_size);
+
+ // The number of items in the EventLog list.
+ size_t payload_count_;
+
+ // The size of the data stored in |payload_|. Used to track where to insert
+ // new data.
+ size_t payload_size_;
+
+ // The payload constructed by calls to log*. The payload may only contain
+ // MAX_EVENT_PAYLOAD (512) bytes.
+ std::unique_ptr<uint8_t[]> payload_;
+
+ DISALLOW_COPY_AND_ASSIGN(EventLogListBuilder);
+};
+
+ #endif // EVENT_LOG_LIST_BUILDER_H_
diff --git a/bootstat/event_log_list_builder_test.cpp b/bootstat/event_log_list_builder_test.cpp
new file mode 100644
index 0000000..affb4bf
--- /dev/null
+++ b/bootstat/event_log_list_builder_test.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2016 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 "event_log_list_builder.h"
+
+#include <inttypes.h>
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+#include <log/log.h>
+
+using testing::ElementsAreArray;
+
+TEST(EventLogListBuilder, Empty) {
+ EventLogListBuilder builder;
+
+ const uint8_t EXPECTED_LOG[] = {
+ EVENT_TYPE_LIST,
+ 0, // Number of items in the list.
+ };
+
+ std::unique_ptr<uint8_t[]> log;
+ size_t size;
+ builder.Release(&log, &size);
+ EXPECT_EQ(2U, size);
+
+ uint8_t* log_data = log.get();
+ EXPECT_THAT(std::vector<uint8_t>(log_data, log_data + size),
+ ElementsAreArray(EXPECTED_LOG));
+}
+
+TEST(EventLogListBuilder, SingleInt) {
+ EventLogListBuilder builder;
+
+ const uint8_t EXPECTED_LOG[] = {
+ EVENT_TYPE_LIST,
+ 1, // Number of items in the list.
+ EVENT_TYPE_INT,
+ 42, 0, 0, 0, // 4 byte integer value.
+ };
+
+ builder.Append(42);
+
+ std::unique_ptr<uint8_t[]> log;
+ size_t size;
+ builder.Release(&log, &size);
+ EXPECT_EQ(7U, size);
+
+ uint8_t* log_data = log.get();
+ EXPECT_THAT(std::vector<uint8_t>(log_data, log_data + size),
+ ElementsAreArray(EXPECTED_LOG));
+}
+
+TEST(EventLogListBuilder, SingleString) {
+ EventLogListBuilder builder;
+
+ const uint8_t EXPECTED_LOG[] = {
+ EVENT_TYPE_LIST,
+ 1, // Number of items in the list.
+ EVENT_TYPE_STRING,
+ 5, 0, 0, 0, // 4 byte length of the string.
+ 'D', 'r', 'o', 'i', 'd',
+ };
+
+ builder.Append("Droid");
+
+ std::unique_ptr<uint8_t[]> log;
+ size_t size;
+ builder.Release(&log, &size);
+ EXPECT_EQ(12U, size);
+
+ uint8_t* log_data = log.get();
+ EXPECT_THAT(std::vector<uint8_t>(log_data, log_data + size),
+ ElementsAreArray(EXPECTED_LOG));
+}
+
+TEST(EventLogListBuilder, IntThenString) {
+ EventLogListBuilder builder;
+
+ const uint8_t EXPECTED_LOG[] = {
+ EVENT_TYPE_LIST,
+ 2, // Number of items in the list.
+ EVENT_TYPE_INT,
+ 42, 0, 0, 0, // 4 byte integer value.
+ EVENT_TYPE_STRING,
+ 5, 0, 0, 0, // 4 byte length of the string.
+ 'D', 'r', 'o', 'i', 'd',
+ };
+
+ builder.Append(42);
+ builder.Append("Droid");
+
+ std::unique_ptr<uint8_t[]> log;
+ size_t size;
+ builder.Release(&log, &size);
+ EXPECT_EQ(17U, size);
+
+ uint8_t* log_data = log.get();
+ EXPECT_THAT(std::vector<uint8_t>(log_data, log_data + size),
+ ElementsAreArray(EXPECTED_LOG));
+}
diff --git a/bootstat/testrunner.cpp b/bootstat/testrunner.cpp
new file mode 100644
index 0000000..79b61d1
--- /dev/null
+++ b/bootstat/testrunner.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 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 <android-base/logging.h>
+#include <gtest/gtest.h>
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ android::base::InitLogging(argv, android::base::StderrLogger);
+ return RUN_ALL_TESTS();
+}
diff --git a/crash_reporter/Android.mk b/crash_reporter/Android.mk
index 4feb72a..fa564b2 100644
--- a/crash_reporter/Android.mk
+++ b/crash_reporter/Android.mk
@@ -135,6 +135,9 @@
include $(CLEAR_VARS)
LOCAL_MODULE := crash_reporter_tests
LOCAL_CPP_EXTENSION := $(crash_reporter_cpp_extension)
+ifdef BRILLO
+LOCAL_MODULE_TAGS := debug
+endif
LOCAL_SHARED_LIBRARIES := libchrome \
libbrillo \
libcutils \
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/crash_reporter/crash_collector_test.cc b/crash_reporter/crash_collector_test.cc
index b55c324..11c8c0d 100644
--- a/crash_reporter/crash_collector_test.cc
+++ b/crash_reporter/crash_collector_test.cc
@@ -20,6 +20,7 @@
#include <utility>
#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <brillo/syslog_logging.h>
@@ -52,20 +53,17 @@
EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(Return());
collector_.Initialize(CountCrash, IsMetrics);
- test_dir_ = FilePath("test");
- base::CreateDirectory(test_dir_);
+ EXPECT_TRUE(test_dir_.CreateUniqueTempDir());
brillo::ClearLog();
}
- void TearDown() {
- base::DeleteFile(test_dir_, true);
- }
-
bool CheckHasCapacity();
protected:
CrashCollectorMock collector_;
- FilePath test_dir_;
+
+ // Temporary directory used for tests.
+ base::ScopedTempDir test_dir_;
};
TEST_F(CrashCollectorTest, Initialize) {
@@ -74,7 +72,7 @@
}
TEST_F(CrashCollectorTest, WriteNewFile) {
- FilePath test_file = test_dir_.Append("test_new");
+ FilePath test_file = test_dir_.path().Append("test_new");
const char kBuffer[] = "buffer";
EXPECT_EQ(strlen(kBuffer),
collector_.WriteNewFile(test_file,
@@ -122,8 +120,10 @@
bool CrashCollectorTest::CheckHasCapacity() {
- static const char kFullMessage[] = "Crash directory test already full";
- bool has_capacity = collector_.CheckHasCapacity(test_dir_);
+ const char* kFullMessage =
+ StringPrintf("Crash directory %s already full",
+ test_dir_.path().value().c_str()).c_str();
+ bool has_capacity = collector_.CheckHasCapacity(test_dir_.path());
bool has_message = FindLog(kFullMessage);
EXPECT_EQ(has_message, !has_capacity);
return has_capacity;
@@ -132,19 +132,22 @@
TEST_F(CrashCollectorTest, CheckHasCapacityUsual) {
// Test kMaxCrashDirectorySize - 1 non-meta files can be added.
for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
- base::WriteFile(test_dir_.Append(StringPrintf("file%d.core", i)), "", 0);
+ base::WriteFile(test_dir_.path().Append(StringPrintf("file%d.core", i)),
+ "", 0);
EXPECT_TRUE(CheckHasCapacity());
}
// Test an additional kMaxCrashDirectorySize - 1 meta files fit.
for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
- base::WriteFile(test_dir_.Append(StringPrintf("file%d.meta", i)), "", 0);
+ base::WriteFile(test_dir_.path().Append(StringPrintf("file%d.meta", i)),
+ "", 0);
EXPECT_TRUE(CheckHasCapacity());
}
// Test an additional kMaxCrashDirectorySize meta files don't fit.
for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize; ++i) {
- base::WriteFile(test_dir_.Append(StringPrintf("overage%d.meta", i)), "", 0);
+ base::WriteFile(test_dir_.path().Append(StringPrintf("overage%d.meta", i)),
+ "", 0);
EXPECT_FALSE(CheckHasCapacity());
}
}
@@ -152,50 +155,52 @@
TEST_F(CrashCollectorTest, CheckHasCapacityCorrectBasename) {
// Test kMaxCrashDirectorySize - 1 files can be added.
for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
- base::WriteFile(test_dir_.Append(StringPrintf("file.%d.core", i)), "", 0);
+ base::WriteFile(test_dir_.path().Append(StringPrintf("file.%d.core", i)),
+ "", 0);
EXPECT_TRUE(CheckHasCapacity());
}
- base::WriteFile(test_dir_.Append("file.last.core"), "", 0);
+ base::WriteFile(test_dir_.path().Append("file.last.core"), "", 0);
EXPECT_FALSE(CheckHasCapacity());
}
TEST_F(CrashCollectorTest, CheckHasCapacityStrangeNames) {
// Test many files with different extensions and same base fit.
for (int i = 0; i < 5 * CrashCollector::kMaxCrashDirectorySize; ++i) {
- base::WriteFile(test_dir_.Append(StringPrintf("a.%d", i)), "", 0);
+ base::WriteFile(test_dir_.path().Append(StringPrintf("a.%d", i)), "", 0);
EXPECT_TRUE(CheckHasCapacity());
}
// Test dot files are treated as individual files.
for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 2; ++i) {
- base::WriteFile(test_dir_.Append(StringPrintf(".file%d", i)), "", 0);
+ base::WriteFile(test_dir_.path().Append(StringPrintf(".file%d", i)), "", 0);
EXPECT_TRUE(CheckHasCapacity());
}
- base::WriteFile(test_dir_.Append("normal.meta"), "", 0);
+ base::WriteFile(test_dir_.path().Append("normal.meta"), "", 0);
EXPECT_FALSE(CheckHasCapacity());
}
TEST_F(CrashCollectorTest, MetaData) {
const char kMetaFileBasename[] = "generated.meta";
- FilePath meta_file = test_dir_.Append(kMetaFileBasename);
- FilePath payload_file = test_dir_.Append("payload-file");
+ FilePath meta_file = test_dir_.path().Append(kMetaFileBasename);
+ FilePath payload_file = test_dir_.path().Append("payload-file");
std::string contents;
const char kPayload[] = "foo";
ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload)));
collector_.AddCrashMetaData("foo", "bar");
collector_.WriteCrashMetaData(meta_file, "kernel", payload_file.value());
EXPECT_TRUE(base::ReadFileToString(meta_file, &contents));
- const char kExpectedMeta[] =
- "foo=bar\n"
- "exec_name=kernel\n"
- "payload=test/payload-file\n"
- "payload_size=3\n"
- "done=1\n";
+ const std::string kExpectedMeta =
+ StringPrintf("foo=bar\n"
+ "exec_name=kernel\n"
+ "payload=%s\n"
+ "payload_size=3\n"
+ "done=1\n",
+ test_dir_.path().Append("payload-file").value().c_str());
EXPECT_EQ(kExpectedMeta, contents);
// Test target of symlink is not overwritten.
- payload_file = test_dir_.Append("payload2-file");
+ payload_file = test_dir_.path().Append("payload2-file");
ASSERT_TRUE(base::WriteFile(payload_file, kPayload, strlen(kPayload)));
- FilePath meta_symlink_path = test_dir_.Append("symlink.meta");
+ FilePath meta_symlink_path = test_dir_.path().Append("symlink.meta");
ASSERT_EQ(0,
symlink(kMetaFileBasename,
meta_symlink_path.value().c_str()));
@@ -221,8 +226,8 @@
}
TEST_F(CrashCollectorTest, GetLogContents) {
- FilePath config_file = test_dir_.Append("crash_config");
- FilePath output_file = test_dir_.Append("crash_log");
+ FilePath config_file = test_dir_.path().Append("crash_config");
+ FilePath output_file = test_dir_.path().Append("crash_log");
const char kConfigContents[] =
"foobar=echo hello there | \\\n sed -e \"s/there/world/\"";
ASSERT_TRUE(
diff --git a/crash_reporter/kernel_collector.cc b/crash_reporter/kernel_collector.cc
index 12b00b9..cb3a315 100644
--- a/crash_reporter/kernel_collector.cc
+++ b/crash_reporter/kernel_collector.cc
@@ -68,8 +68,8 @@
" RIP \\[<.*>\\] ([^\\+ ]+).*", // X86_64 uses RIP for the program counter
};
-COMPILE_ASSERT(arraysize(kPCRegex) == KernelCollector::kArchCount,
- missing_arch_pc_regexp);
+static_assert(arraysize(kPCRegex) == KernelCollector::kArchCount,
+ "Missing Arch PC regexp");
} // namespace
diff --git a/crash_reporter/list_proxies.cc b/crash_reporter/list_proxies.cc
index d445557..3374b5f 100644
--- a/crash_reporter/list_proxies.cc
+++ b/crash_reporter/list_proxies.cc
@@ -75,13 +75,12 @@
// Start by finding the first space (if any).
std::string::iterator space;
for (space = token.begin(); space != token.end(); ++space) {
- if (IsAsciiWhitespace(*space)) {
+ if (base::IsAsciiWhitespace(*space)) {
break;
}
}
- std::string scheme = std::string(token.begin(), space);
- base::StringToLowerASCII(&scheme);
+ std::string scheme = base::ToLowerASCII(std::string(token.begin(), space));
// Chrome uses "socks" to mean socks4 and "proxy" to mean http.
if (scheme == "socks")
scheme += "4";
@@ -183,7 +182,7 @@
timeout_callback_.Cancel();
proxies_ = ParseProxyString(proxy_info);
LOG(INFO) << "Found proxies via browser signal: "
- << JoinString(proxies_, 'x');
+ << base::JoinString(proxies_, "x");
Quit();
}
diff --git a/crash_reporter/unclean_shutdown_collector_test.cc b/crash_reporter/unclean_shutdown_collector_test.cc
index 3bdeca1..56d2704 100644
--- a/crash_reporter/unclean_shutdown_collector_test.cc
+++ b/crash_reporter/unclean_shutdown_collector_test.cc
@@ -19,6 +19,7 @@
#include <unistd.h>
#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
#include <base/strings/string_util.h>
#include <brillo/syslog_logging.h>
#include <gmock/gmock.h>
@@ -32,10 +33,6 @@
int s_crashes = 0;
bool s_metrics = true;
-const char kTestDirectory[] = "test";
-const char kTestSuspended[] = "test/suspended";
-const char kTestUnclean[] = "test/unclean";
-
void CountCrash() {
++s_crashes;
}
@@ -59,12 +56,17 @@
collector_.Initialize(CountCrash,
IsMetrics);
- rmdir(kTestDirectory);
- test_unclean_ = FilePath(kTestUnclean);
- collector_.unclean_shutdown_file_ = kTestUnclean;
+
+ EXPECT_TRUE(test_dir_.CreateUniqueTempDir());
+
+ test_directory_ = test_dir_.path().Append("test");
+ test_unclean_ = test_dir_.path().Append("test/unclean");
+
+ collector_.unclean_shutdown_file_ = test_unclean_.value().c_str();
base::DeleteFile(test_unclean_, true);
// Set up an alternate power manager state file as well
- collector_.powerd_suspended_file_ = FilePath(kTestSuspended);
+ collector_.powerd_suspended_file_ =
+ test_dir_.path().Append("test/suspended");
brillo::ClearLog();
}
@@ -75,6 +77,10 @@
}
UncleanShutdownCollectorMock collector_;
+
+ // Temporary directory used for tests.
+ base::ScopedTempDir test_dir_;
+ FilePath test_directory_;
FilePath test_unclean_;
};
@@ -84,7 +90,7 @@
}
TEST_F(UncleanShutdownCollectorTest, EnableWithParent) {
- mkdir(kTestDirectory, 0777);
+ mkdir(test_directory_.value().c_str(), 0777);
ASSERT_TRUE(collector_.Enable());
ASSERT_TRUE(base::PathExists(test_unclean_));
}
@@ -133,15 +139,15 @@
}
TEST_F(UncleanShutdownCollectorTest, CantDisable) {
- mkdir(kTestDirectory, 0700);
- if (mkdir(kTestUnclean, 0700)) {
+ mkdir(test_directory_.value().c_str(), 0700);
+ if (mkdir(test_unclean_.value().c_str(), 0700)) {
ASSERT_EQ(EEXIST, errno)
- << "Error while creating directory '" << kTestUnclean
+ << "Error while creating directory '" << test_unclean_.value()
<< "': " << strerror(errno);
}
ASSERT_EQ(0, base::WriteFile(test_unclean_.Append("foo"), "", 0))
<< "Error while creating empty file '"
<< test_unclean_.Append("foo").value() << "': " << strerror(errno);
ASSERT_FALSE(collector_.Disable());
- rmdir(kTestUnclean);
+ rmdir(test_unclean_.value().c_str());
}
diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc
index 6714f52..98d7448 100644
--- a/crash_reporter/user_collector.cc
+++ b/crash_reporter/user_collector.cc
@@ -151,8 +151,8 @@
return false;
}
std::string id_substring = id_line.substr(strlen(prefix), std::string::npos);
- std::vector<std::string> ids;
- base::SplitString(id_substring, '\t', &ids);
+ std::vector<std::string> ids = base::SplitString(
+ id_substring, "\t", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
if (ids.size() != kIdMax || kind < 0 || kind >= kIdMax) {
return false;
}
@@ -313,8 +313,8 @@
uid_t uid;
if (base::ReadFileToString(process_path.Append("status"), &status)) {
- std::vector<std::string> status_lines;
- base::SplitString(status, '\n', &status_lines);
+ std::vector<std::string> status_lines = base::SplitString(
+ status, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
std::string process_state;
if (!GetStateFromStatus(status_lines, &process_state)) {
diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc
index 638ea34..d9c9a5b 100644
--- a/crash_reporter/user_collector_test.cc
+++ b/crash_reporter/user_collector_test.cc
@@ -65,8 +65,10 @@
false,
false,
"");
- base::DeleteFile(FilePath("test"), true);
- mkdir("test", 0777);
+
+ EXPECT_TRUE(test_dir_.CreateUniqueTempDir());
+
+ mkdir(test_dir_.path().Append("test").value().c_str(), 0777);
pid_ = getpid();
brillo::ClearLog();
}
@@ -80,13 +82,13 @@
}
std::vector<std::string> SplitLines(const std::string &lines) const {
- std::vector<std::string> result;
- base::SplitString(lines, '\n', &result);
- return result;
+ return base::SplitString(lines, "\n", base::TRIM_WHITESPACE,
+ base::SPLIT_WANT_ALL);
}
UserCollectorMock collector_;
pid_t pid_;
+ base::ScopedTempDir test_dir_;
};
TEST_F(UserCollectorTest, ParseCrashAttributes) {
@@ -173,14 +175,15 @@
&result));
ASSERT_TRUE(FindLog(
"Readlink failed on /does_not_exist with 2"));
- std::string long_link;
+ std::string long_link = test_dir_.path().value();
for (int i = 0; i < 50; ++i)
long_link += "0123456789";
long_link += "/gold";
for (size_t len = 1; len <= long_link.size(); ++len) {
std::string this_link;
- static const char kLink[] = "test/this_link";
+ static const char* kLink =
+ test_dir_.path().Append("test/this_link").value().c_str();
this_link.assign(long_link.c_str(), len);
ASSERT_EQ(len, this_link.size());
unlink(kLink);
@@ -341,13 +344,13 @@
}
TEST_F(UserCollectorTest, CopyOffProcFilesBadPid) {
- FilePath container_path("test/container");
+ FilePath container_path(test_dir_.path().Append("test/container"));
ASSERT_FALSE(collector_.CopyOffProcFiles(0, container_path));
EXPECT_TRUE(FindLog("Path /proc/0 does not exist"));
}
TEST_F(UserCollectorTest, CopyOffProcFilesOK) {
- FilePath container_path("test/container");
+ FilePath container_path(test_dir_.path().Append("test/container"));
ASSERT_TRUE(collector_.CopyOffProcFiles(pid_, container_path));
EXPECT_FALSE(FindLog("Could not copy"));
static struct {
@@ -371,9 +374,7 @@
}
TEST_F(UserCollectorTest, ValidateProcFiles) {
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath container_dir = temp_dir.path();
+ FilePath container_dir = test_dir_.path();
// maps file not exists (i.e. GetFileSize fails)
EXPECT_FALSE(collector_.ValidateProcFiles(container_dir));
@@ -392,9 +393,7 @@
}
TEST_F(UserCollectorTest, ValidateCoreFile) {
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath container_dir = temp_dir.path();
+ FilePath container_dir = test_dir_.path();
FilePath core_file = container_dir.Append("core");
// Core file does not exist
diff --git a/fastboot/.clang-format b/fastboot/.clang-format
new file mode 100644
index 0000000..6737535
--- /dev/null
+++ b/fastboot/.clang-format
@@ -0,0 +1,12 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 4
+PointerAlignment: Left
+TabWidth: 4
+UseTab: Never
+PenaltyExcessCharacter: 32
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index ac5a17a..47567c0 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -157,17 +157,17 @@
a->msg = mkmsg("writing '%s'", ptn);
}
-void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz)
-{
+void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, unsigned sz, size_t current,
+ size_t total) {
Action *a;
a = queue_action(OP_DOWNLOAD_SPARSE, "");
a->data = s;
a->size = 0;
- a->msg = mkmsg("sending sparse '%s' (%d KB)", ptn, sz / 1024);
+ a->msg = mkmsg("sending sparse '%s' %zu/%zu (%d KB)", ptn, current, total, sz / 1024);
a = queue_action(OP_COMMAND, "flash:%s", ptn);
- a->msg = mkmsg("writing '%s'", ptn);
+ a->msg = mkmsg("writing '%s' %zu/%zu", ptn, current, total);
}
static int match(const char* str, const char** value, unsigned count) {
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index a66a45a..e409bf6 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -43,6 +43,8 @@
#include <sys/types.h>
#include <unistd.h>
#include <functional>
+#include <utility>
+#include <vector>
#include <android-base/parseint.h>
#include <android-base/strings.h>
@@ -765,13 +767,22 @@
sparse_file** s;
switch (buf->type) {
- case FB_BUFFER_SPARSE:
+ case FB_BUFFER_SPARSE: {
+ std::vector<std::pair<sparse_file*, int64_t>> sparse_files;
s = reinterpret_cast<sparse_file**>(buf->data);
while (*s) {
int64_t sz = sparse_file_len(*s, true, false);
- fb_queue_flash_sparse(pname, *s++, sz);
+ sparse_files.emplace_back(*s, sz);
+ ++s;
+ }
+
+ for (size_t i = 0; i < sparse_files.size(); ++i) {
+ const auto& pair = sparse_files[i];
+ fb_queue_flash_sparse(pname, pair.first, pair.second, i + 1, sparse_files.size());
}
break;
+ }
+
case FB_BUFFER:
fb_queue_flash(pname, buf->data, buf->sz);
break;
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index acfbc13..1932bab 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -51,7 +51,8 @@
/* engine.c - high level command queue engine */
bool fb_getvar(Transport* transport, const std::string& key, std::string* value);
void fb_queue_flash(const char *ptn, void *data, uint32_t sz);
-void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, uint32_t sz);
+void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, uint32_t sz, size_t current,
+ size_t total);
void fb_queue_erase(const char *ptn);
void fb_queue_format(const char *ptn, int skip_if_not_supported, int32_t max_chunk_sz);
void fb_queue_require(const char *prod, const char *var, bool invert,
diff --git a/include/log/log.h b/include/log/log.h
index 3d9240d..1bd9165 100644
--- a/include/log/log.h
+++ b/include/log/log.h
@@ -585,14 +585,6 @@
(__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE) != 0)
#endif
-// TODO: remove these prototypes and their users
-#define android_writevLog(vec,num) do{}while(0)
-#define android_write1Log(str,len) do{}while (0)
-#define android_setMinPriority(tag, prio) do{}while(0)
-//#define android_logToCallback(func) do{}while(0)
-#define android_logToFile(tag, file) (0)
-#define android_logToFd(tag, fd) (0)
-
typedef enum log_id {
LOG_ID_MIN = 0,
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/include/ziparchive/zip_writer.h b/include/ziparchive/zip_writer.h
index 0efade8..0b6ede4 100644
--- a/include/ziparchive/zip_writer.h
+++ b/include/ziparchive/zip_writer.h
@@ -87,11 +87,27 @@
int32_t StartEntry(const char* path, size_t flags);
/**
+ * Starts a new zip entry with the given path and flags, where the
+ * entry will be aligned to the given alignment.
+ * Flags can only be ZipWriter::kCompress. Using the flag ZipWriter::kAlign32
+ * will result in an error.
+ * Subsequent calls to WriteBytes(const void*, size_t) will add data to this entry.
+ * Returns 0 on success, and an error value < 0 on failure.
+ */
+ int32_t StartAlignedEntry(const char* path, size_t flags, uint32_t alignment);
+
+ /**
* Same as StartEntry(const char*, size_t), but sets a last modified time for the entry.
*/
int32_t StartEntryWithTime(const char* path, size_t flags, time_t time);
/**
+ * Same as StartAlignedEntry(const char*, size_t), but sets a last modified time for the entry.
+ */
+ int32_t StartAlignedEntryWithTime(const char* path, size_t flags, time_t time,
+ uint32_t alignment);
+
+ /**
* Writes bytes to the zip file for the previously started zip entry.
* Returns 0 on success, and an error value < 0 on failure.
*/
diff --git a/init/builtins.cpp b/init/builtins.cpp
index a81d06d..5918609 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -418,20 +418,32 @@
while (1) { pause(); } // never reached
}
-static void import_late() {
- static const std::vector<std::string> init_directories = {
- "/system/etc/init",
- "/vendor/etc/init",
- "/odm/etc/init"
- };
-
+/* Imports .rc files from the specified paths. Default ones are applied if none is given.
+ *
+ * start_index: index of the first path in the args list
+ */
+static void import_late(const std::vector<std::string>& args, size_t start_index) {
Parser& parser = Parser::GetInstance();
- for (const auto& dir : init_directories) {
- parser.ParseConfig(dir.c_str());
+ if (args.size() <= start_index) {
+ // Use the default set if no path is given
+ static const std::vector<std::string> init_directories = {
+ "/system/etc/init",
+ "/vendor/etc/init",
+ "/odm/etc/init"
+ };
+
+ for (const auto& dir : init_directories) {
+ parser.ParseConfig(dir);
+ }
+ } else {
+ for (size_t i = start_index; i < args.size(); ++i) {
+ parser.ParseConfig(args[i]);
+ }
}
}
-/*
+/* mount_all <fstab> [ <path> ]*
+ *
* This function might request a reboot, in which case it will
* not return.
*/
@@ -478,7 +490,8 @@
return -1;
}
- import_late();
+ /* Paths of .rc files are specified at the 2nd argument and beyond */
+ import_late(args, 2);
if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
property_set("vold.decrypt", "trigger_encryption");
@@ -893,7 +906,7 @@
{"load_system_props", {0, 0, do_load_system_props}},
{"loglevel", {1, 1, do_loglevel}},
{"mkdir", {1, 4, do_mkdir}},
- {"mount_all", {1, 1, do_mount_all}},
+ {"mount_all", {1, kMax, do_mount_all}},
{"mount", {3, kMax, do_mount}},
{"powerctl", {1, 1, do_powerctl}},
{"restart", {1, 1, do_restart}},
diff --git a/init/log.cpp b/init/log.cpp
index a72906b..ace9fd7 100644
--- a/init/log.cpp
+++ b/init/log.cpp
@@ -27,6 +27,8 @@
static void init_klog_vwrite(int level, const char* fmt, va_list ap) {
static const char* tag = basename(getprogname());
+ if (level > klog_get_level()) return;
+
// The kernel's printk buffer is only 1024 bytes.
// TODO: should we automatically break up long lines into multiple lines?
// Or we could log but with something like "..." at the end?
diff --git a/init/readme.txt b/init/readme.txt
index bacd6bd..5a1cf44 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -49,6 +49,8 @@
actions or daemons needed for motion sensor or other peripheral
functionality.
+One may specify paths in the mount_all command line to have it import
+.rc files at the specified paths instead of the default ones described above.
Actions
-------
@@ -263,8 +265,10 @@
owned by the root user and root group. If provided, the mode, owner and group
will be updated if the directory exists already.
-mount_all <fstab>
- Calls fs_mgr_mount_all on the given fs_mgr-format fstab.
+mount_all <fstab> [ <path> ]*
+ Calls fs_mgr_mount_all on the given fs_mgr-format fstab and imports .rc files
+ at the specified paths (e.g., on the partitions just mounted). Refer to the
+ section of "Init .rc Files" for detail.
mount <type> <device> <dir> [ <flag> ]* [<options>]
Attempt to mount the named device at the directory <dir>
@@ -358,7 +362,8 @@
There are only two times where the init executable imports .rc files,
1) When it imports /init.rc during initial boot
- 2) When it imports /{system,vendor,odm}/etc/init/ during mount_all
+ 2) When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
+ paths during mount_all
Properties
diff --git a/libcutils/klog.c b/libcutils/klog.c
index 710dc66..7402903 100644
--- a/libcutils/klog.c
+++ b/libcutils/klog.c
@@ -62,6 +62,7 @@
}
void klog_write(int level, const char* fmt, ...) {
+ if (level > klog_level) return;
char buf[LOG_BUF_MAX];
va_list ap;
va_start(ap, fmt);
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/liblog/logprint.c b/liblog/logprint.c
index bd36cdd..4ef62a1 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -1488,7 +1488,7 @@
strcat(p, suffixBuf);
p += suffixLen;
} else {
- while(pm < (entry->message + entry->messageLen)) {
+ do {
const char *lineStart;
size_t lineLen;
lineStart = pm;
@@ -1510,7 +1510,7 @@
p += suffixLen;
if (*pm == '\n') pm++;
- }
+ } while (pm < (entry->message + entry->messageLen));
}
if (p_outLength != NULL) {
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 50afc5f..a936455 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -165,6 +165,133 @@
android_logger_list_close(logger_list);
}
+static inline int32_t get4LE(const char* src)
+{
+ return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+TEST(liblog, __android_log_bswrite) {
+ struct logger_list *logger_list;
+
+ pid_t pid = getpid();
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+ static const char buffer[] = "Hello World";
+ log_time ts(android_log_clockid());
+
+ ASSERT_LT(0, __android_log_bswrite(0, buffer));
+ usleep(1000000);
+
+ int count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.sec < (ts.tv_sec - 1))
+ || ((ts.tv_sec + 1) < log_msg.entry.sec)
+ || (log_msg.entry.len != (4 + 1 + 4 + sizeof(buffer) - 1))
+ || (log_msg.id() != LOG_ID_EVENTS)) {
+ continue;
+ }
+
+ char *eventData = log_msg.msg();
+
+ if (eventData[4] != EVENT_TYPE_STRING) {
+ continue;
+ }
+
+ int len = get4LE(eventData + 4 + 1);
+ if (len == (sizeof(buffer) - 1)) {
+ ++count;
+
+ AndroidLogFormat *logformat = android_log_format_new();
+ EXPECT_TRUE(NULL != logformat);
+ AndroidLogEntry entry;
+ char msgBuf[1024];
+ EXPECT_EQ(0, android_log_processBinaryLogBuffer(&log_msg.entry_v1,
+ &entry,
+ NULL,
+ msgBuf,
+ sizeof(msgBuf)));
+ fflush(stderr);
+ EXPECT_EQ(31, android_log_printLogLine(logformat, fileno(stderr), &entry));
+ android_log_format_free(logformat);
+ }
+ }
+
+ EXPECT_EQ(1, count);
+
+ android_logger_list_close(logger_list);
+}
+
+TEST(liblog, __android_log_bswrite__empty_string) {
+ struct logger_list *logger_list;
+
+ pid_t pid = getpid();
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+ static const char buffer[] = "";
+ log_time ts(android_log_clockid());
+
+ ASSERT_LT(0, __android_log_bswrite(0, buffer));
+ usleep(1000000);
+
+ int count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.sec < (ts.tv_sec - 1))
+ || ((ts.tv_sec + 1) < log_msg.entry.sec)
+ || (log_msg.entry.len != (4 + 1 + 4))
+ || (log_msg.id() != LOG_ID_EVENTS)) {
+ continue;
+ }
+
+ char *eventData = log_msg.msg();
+
+ if (eventData[4] != EVENT_TYPE_STRING) {
+ continue;
+ }
+
+ int len = get4LE(eventData + 4 + 1);
+ if (len == 0) {
+ ++count;
+
+ AndroidLogFormat *logformat = android_log_format_new();
+ EXPECT_TRUE(NULL != logformat);
+ AndroidLogEntry entry;
+ char msgBuf[1024];
+ EXPECT_EQ(0, android_log_processBinaryLogBuffer(&log_msg.entry_v1,
+ &entry,
+ NULL,
+ msgBuf,
+ sizeof(msgBuf)));
+ fflush(stderr);
+ EXPECT_EQ(20, android_log_printLogLine(logformat, fileno(stderr), &entry));
+ android_log_format_free(logformat);
+ }
+ }
+
+ EXPECT_EQ(1, count);
+
+ android_logger_list_close(logger_list);
+}
+
TEST(liblog, __security) {
static const char persist_key[] = "persist.logd.security";
static const char readonly_key[] = "ro.device_owner";
@@ -1106,11 +1233,6 @@
property_set(key + base_offset, hold[3]);
}
-static inline int32_t get4LE(const char* src)
-{
- return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
-}
-
TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__typical) {
const int TAG = 123456781;
const char SUBTAG[] = "test-subtag";
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index 32a65ea..4eff891 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -413,14 +413,19 @@
if (errno == ENOENT) {
if (mkdir(app_code_cache_dir, S_IRWXU | S_IRWXG | S_IXOTH) == -1) {
ALOGW("Cannot create code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
+ fprintf(stderr, "Cannot create code cache directory %s: %s.",
+ app_code_cache_dir, strerror(errno));
ReleaseAppCodeCacheDir();
}
} else {
ALOGW("Cannot stat code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
+ fprintf(stderr, "Cannot stat code cache directory %s: %s.",
+ app_code_cache_dir, strerror(errno));
ReleaseAppCodeCacheDir();
}
} else if (!S_ISDIR(st.st_mode)) {
ALOGW("Code cache is not a directory %s.", app_code_cache_dir);
+ fprintf(stderr, "Code cache is not a directory %s.", app_code_cache_dir);
ReleaseAppCodeCacheDir();
}
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index f117cc5..1ebed30 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -20,12 +20,19 @@
#include <utils/Log.h>
+#include <sys/param.h>
+
#include <cassert>
#include <cstdio>
#include <memory>
+#include <vector>
#include <zlib.h>
#define DEF_MEM_LEVEL 8 // normally in zutil.h?
+#if !defined(powerof2)
+#define powerof2(x) ((((x)-1)&(x))==0)
+#endif
+
/* Zip compression methods we support */
enum {
kCompressStored = 0, // no compression
@@ -50,6 +57,12 @@
// An error occurred in zlib.
static const int32_t kZlibError = -4;
+// The start aligned function was called with the aligned flag.
+static const int32_t kInvalidAlign32Flag = -5;
+
+// The alignment parameter is not a power of 2.
+static const int32_t kInvalidAlignment = -6;
+
static const char* sErrorCodes[] = {
"Invalid state",
"IO error",
@@ -102,7 +115,25 @@
}
int32_t ZipWriter::StartEntry(const char* path, size_t flags) {
- return StartEntryWithTime(path, flags, time_t());
+ uint32_t alignment = 0;
+ if (flags & kAlign32) {
+ flags &= ~kAlign32;
+ alignment = 4;
+ }
+ return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
+}
+
+int32_t ZipWriter::StartAlignedEntry(const char* path, size_t flags, uint32_t alignment) {
+ return StartAlignedEntryWithTime(path, flags, time_t(), alignment);
+}
+
+int32_t ZipWriter::StartEntryWithTime(const char* path, size_t flags, time_t time) {
+ uint32_t alignment = 0;
+ if (flags & kAlign32) {
+ flags &= ~kAlign32;
+ alignment = 4;
+ }
+ return StartAlignedEntryWithTime(path, flags, time, alignment);
}
static void ExtractTimeAndDate(time_t when, uint16_t* out_time, uint16_t* out_date) {
@@ -126,11 +157,20 @@
*out_time = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
}
-int32_t ZipWriter::StartEntryWithTime(const char* path, size_t flags, time_t time) {
+int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags,
+ time_t time, uint32_t alignment) {
if (state_ != State::kWritingZip) {
return kInvalidState;
}
+ if (flags & kAlign32) {
+ return kInvalidAlign32Flag;
+ }
+
+ if (powerof2(alignment) == 0) {
+ return kInvalidAlignment;
+ }
+
FileInfo fileInfo = {};
fileInfo.path = std::string(path);
fileInfo.local_file_header_offset = current_offset_;
@@ -166,11 +206,14 @@
header.file_name_length = fileInfo.path.size();
off64_t offset = current_offset_ + sizeof(header) + fileInfo.path.size();
- if ((flags & ZipWriter::kAlign32) && (offset & 0x03)) {
+ std::vector<char> zero_padding;
+ if (alignment != 0 && (offset & (alignment - 1))) {
// Pad the extra field so the data will be aligned.
- uint16_t padding = 4 - (offset % 4);
+ uint16_t padding = alignment - (offset % alignment);
header.extra_field_length = padding;
offset += padding;
+ zero_padding.resize(padding);
+ memset(zero_padding.data(), 0, zero_padding.size());
}
if (fwrite(&header, sizeof(header), 1, file_) != 1) {
@@ -181,7 +224,9 @@
return HandleError(kIoError);
}
- if (fwrite("\0\0\0", 1, header.extra_field_length, file_) != header.extra_field_length) {
+ if (header.extra_field_length != 0 &&
+ fwrite(zero_padding.data(), 1, header.extra_field_length, file_)
+ != header.extra_field_length) {
return HandleError(kIoError);
}
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index b7d1458..fe0846d 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -19,6 +19,7 @@
#include <android-base/test_utils.h>
#include <gtest/gtest.h>
+#include <time.h>
#include <memory>
#include <vector>
@@ -122,7 +123,7 @@
CloseArchive(handle);
}
-TEST_F(zipwriter, WriteUncompressedZipWithAlignedFile) {
+TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlag) {
ZipWriter writer(file_);
ASSERT_EQ(0, writer.StartEntry("align.txt", ZipWriter::kAlign32));
@@ -142,6 +143,103 @@
CloseArchive(handle);
}
+void ConvertZipTimeToTm(uint32_t& zip_time, struct tm* tm) {
+ memset(tm, 0, sizeof(struct tm));
+ tm->tm_hour = (zip_time >> 11) & 0x1f;
+ tm->tm_min = (zip_time >> 5) & 0x3f;
+ tm->tm_sec = (zip_time & 0x1f) << 1;
+
+ tm->tm_year = ((zip_time >> 25) & 0x7f) + 80;
+ tm->tm_mon = ((zip_time >> 21) & 0xf) - 1;
+ tm->tm_mday = (zip_time >> 16) & 0x1f;
+}
+
+TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlagAndTime) {
+ ZipWriter writer(file_);
+
+ struct tm tm;
+ memset(&tm, 0, sizeof(struct tm));
+ ASSERT_TRUE(strptime("18:30:20 1/12/2001", "%H:%M:%S %d/%m/%Y", &tm) != nullptr);
+ time_t time = mktime(&tm);
+ ASSERT_EQ(0, writer.StartEntryWithTime("align.txt", ZipWriter::kAlign32, time));
+ ASSERT_EQ(0, writer.WriteBytes("he", 2));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ EXPECT_EQ(0, data.offset & 0x03);
+
+ struct tm mod;
+ ConvertZipTimeToTm(data.mod_time, &mod);
+ EXPECT_EQ(tm.tm_sec, mod.tm_sec);
+ EXPECT_EQ(tm.tm_min, mod.tm_min);
+ EXPECT_EQ(tm.tm_hour, mod.tm_hour);
+ EXPECT_EQ(tm.tm_mday, mod.tm_mday);
+ EXPECT_EQ(tm.tm_mon, mod.tm_mon);
+ EXPECT_EQ(tm.tm_year, mod.tm_year);
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValue) {
+ ZipWriter writer(file_);
+
+ ASSERT_EQ(0, writer.StartAlignedEntry("align.txt", 0, 4096));
+ ASSERT_EQ(0, writer.WriteBytes("he", 2));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ EXPECT_EQ(0, data.offset & 0xfff);
+
+ CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValueAndTime) {
+ ZipWriter writer(file_);
+
+ struct tm tm;
+ memset(&tm, 0, sizeof(struct tm));
+ ASSERT_TRUE(strptime("18:30:20 1/12/2001", "%H:%M:%S %d/%m/%Y", &tm) != nullptr);
+ time_t time = mktime(&tm);
+ ASSERT_EQ(0, writer.StartAlignedEntryWithTime("align.txt", 0, time, 4096));
+ ASSERT_EQ(0, writer.WriteBytes("he", 2));
+ ASSERT_EQ(0, writer.FinishEntry());
+ ASSERT_EQ(0, writer.Finish());
+
+ ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+ ZipEntry data;
+ ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+ EXPECT_EQ(0, data.offset & 0xfff);
+
+ struct tm mod;
+ ConvertZipTimeToTm(data.mod_time, &mod);
+ EXPECT_EQ(tm.tm_sec, mod.tm_sec);
+ EXPECT_EQ(tm.tm_min, mod.tm_min);
+ EXPECT_EQ(tm.tm_hour, mod.tm_hour);
+ EXPECT_EQ(tm.tm_mday, mod.tm_mday);
+ EXPECT_EQ(tm.tm_mon, mod.tm_mon);
+ EXPECT_EQ(tm.tm_year, mod.tm_year);
+
+ CloseArchive(handle);
+}
+
TEST_F(zipwriter, WriteCompressedZipWithOneFile) {
ZipWriter writer(file_);
@@ -206,3 +304,10 @@
CloseArchive(handle);
}
+
+TEST_F(zipwriter, CheckStartEntryErrors) {
+ ZipWriter writer(file_);
+
+ ASSERT_EQ(-5, writer.StartAlignedEntry("align.txt", ZipWriter::kAlign32, 4096));
+ ASSERT_EQ(-6, writer.StartAlignedEntry("align.txt", 0, 3));
+}
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index cdf5d08..9e0d451 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -332,13 +332,21 @@
}
}
- bool setLast = mLastSet[id] && (it == mLast[id]);
+ bool setLast[LOG_ID_MAX];
+ bool doSetLast = false;
+ log_id_for_each(i) {
+ doSetLast |= setLast[i] = mLastSet[i] && (it == mLast[i]);
+ }
it = mLogElements.erase(it);
- if (setLast) {
- if (it == mLogElements.end()) { // unlikely
- mLastSet[id] = false;
- } else {
- mLast[id] = it;
+ if (doSetLast) {
+ log_id_for_each(i) {
+ if (setLast[i]) {
+ if (it == mLogElements.end()) { // unlikely
+ mLastSet[i] = false;
+ } else {
+ mLast[i] = it;
+ }
+ }
}
}
if (coalesce) {
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
index 7381703..250c657 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 \
@@ -200,6 +202,9 @@
LOCAL_SHARED_LIBRARIES := $(metricsd_shared_libraries)
LOCAL_SRC_FILES := $(metricsd_tests_sources) $(metricsd_common)
LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock metricsd_protos metricsd_binder_proxy
+ifdef BRILLO
+LOCAL_MODULE_TAGS := debug
+endif
include $(BUILD_NATIVE_TEST)
# Unit tests for metrics_collector.
@@ -215,6 +220,9 @@
$(metrics_collector_common)
LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock metricsd_binder_proxy \
$(metrics_collector_static_libraries)
+ifdef BRILLO
+LOCAL_MODULE_TAGS := debug
+endif
include $(BUILD_NATIVE_TEST)
# Weave schema files
diff --git a/metricsd/collectors/cpu_usage_collector.cc b/metricsd/collectors/cpu_usage_collector.cc
index 05934b4..9b0bb34 100644
--- a/metricsd/collectors/cpu_usage_collector.cc
+++ b/metricsd/collectors/cpu_usage_collector.cc
@@ -104,14 +104,15 @@
uint64_t *user_ticks,
uint64_t *user_nice_ticks,
uint64_t *system_ticks) {
- std::vector<std::string> proc_stat_lines;
- base::SplitString(stat_content, '\n', &proc_stat_lines);
+ std::vector<std::string> proc_stat_lines = base::SplitString(
+ stat_content, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
if (proc_stat_lines.empty()) {
LOG(WARNING) << "No lines found in " << kMetricsProcStatFileName;
return false;
}
- std::vector<std::string> proc_stat_totals;
- base::SplitStringAlongWhitespace(proc_stat_lines[0], &proc_stat_totals);
+ std::vector<std::string> proc_stat_totals =
+ base::SplitString(proc_stat_lines[0], base::kWhitespaceASCII,
+ base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
if (proc_stat_totals.size() != kMetricsProcStatFirstLineItemsCount ||
proc_stat_totals[0] != "cpu" ||
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/libmetrics-334380.gyp b/metricsd/libmetrics-369476.gyp
similarity index 72%
rename from metricsd/libmetrics-334380.gyp
rename to metricsd/libmetrics-369476.gyp
index 9771821..b545d35 100644
--- a/metricsd/libmetrics-334380.gyp
+++ b/metricsd/libmetrics-369476.gyp
@@ -1,6 +1,6 @@
{
'variables': {
- 'libbase_ver': 334380,
+ 'libbase_ver': 369476,
},
'includes': [
'libmetrics.gypi',
diff --git a/metricsd/metrics_collector.cc b/metricsd/metrics_collector.cc
index 2cf2338..ec7e040 100644
--- a/metricsd/metrics_collector.cc
+++ b/metricsd/metrics_collector.cc
@@ -534,18 +534,20 @@
bool MetricsCollector::FillMeminfo(const string& meminfo_raw,
vector<MeminfoRecord>* fields) {
- vector<string> lines;
- unsigned int nlines = Tokenize(meminfo_raw, "\n", &lines);
+ vector<std::string> lines =
+ base::SplitString(meminfo_raw, "\n", base::KEEP_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY);
// Scan meminfo output and collect field values. Each field name has to
// match a meminfo entry (case insensitive) after removing non-alpha
// characters from the entry.
- unsigned int ifield = 0;
- for (unsigned int iline = 0;
- iline < nlines && ifield < fields->size();
+ size_t ifield = 0;
+ for (size_t iline = 0;
+ iline < lines.size() && ifield < fields->size();
iline++) {
- vector<string> tokens;
- Tokenize(lines[iline], ": ", &tokens);
+ vector<string> tokens =
+ base::SplitString(lines[iline], ": ", base::KEEP_WHITESPACE,
+ base::SPLIT_WANT_NONEMPTY);
if (strcmp((*fields)[ifield].match, tokens[0].c_str()) == 0) {
// Name matches. Parse value and save.
if (!base::StringToInt(tokens[1], &(*fields)[ifield].value)) {
diff --git a/metricsd/metrics_collector_main.cc b/metricsd/metrics_collector_main.cc
index d7aaaf5..14bb935 100644
--- a/metricsd/metrics_collector_main.cc
+++ b/metricsd/metrics_collector_main.cc
@@ -41,7 +41,8 @@
}
dev_path = dev_path_cstr;
// Check that rootdev begins with "/dev/block/".
- if (!base::StartsWithASCII(dev_path, dev_prefix, false)) {
+ if (!base::StartsWith(dev_path, dev_prefix,
+ base::CompareCase::INSENSITIVE_ASCII)) {
LOG(WARNING) << "unexpected root device " << dev_path;
return "";
}
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..5a759d3
--- /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_->QuitWhenIdleClosure());
+
+ 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..0dc59a4 100644
--- a/metricsd/uploader/upload_service.cc
+++ b/metricsd/uploader/upload_service.cc
@@ -42,49 +42,89 @@
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();
+ PersistToDisk();
+}
+
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 +218,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_));
+}