Merge "fastboot: Delete logical "other" partitions on retrofit devices."
diff --git a/adb/fastdeploy/OWNERS b/adb/fastdeploy/OWNERS
new file mode 100644
index 0000000..d145834
--- /dev/null
+++ b/adb/fastdeploy/OWNERS
@@ -0,0 +1 @@
+idries@google.com
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 577e336..d79d20b 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -348,8 +348,16 @@
return vm_pid;
}
+static void InstallSigPipeHandler() {
+ struct sigaction action = {};
+ action.sa_handler = SIG_IGN;
+ action.sa_flags = SA_RESTART;
+ sigaction(SIGPIPE, &action, nullptr);
+}
+
int main(int argc, char** argv) {
DefuseSignalHandlers();
+ InstallSigPipeHandler();
atrace_begin(ATRACE_TAG, "before reparent");
pid_t target_process = getppid();
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 23e9c51..bef46e1 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -790,6 +790,8 @@
}
std::vector<std::string> fs_mgr_overlayfs_required_devices(fstab* fstab) {
+ if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return {};
+
if (fs_mgr_get_entry_for_mount_point(const_cast<struct fstab*>(fstab), kScratchMountPoint)) {
return {};
}
diff --git a/init/Android.bp b/init/Android.bp
index ff3b61f..ea66ac6 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -71,6 +71,7 @@
"libbinder",
"libbootloader_message",
"libcutils",
+ "libcrypto",
"libdl",
"libext4_utils",
"libfs_mgr",
@@ -79,6 +80,7 @@
"libkeyutils",
"liblog",
"liblogwrap",
+ "liblp",
"libselinux",
"libutils",
],
@@ -92,6 +94,7 @@
"action.cpp",
"action_manager.cpp",
"action_parser.cpp",
+ "boringssl_self_test.cpp",
"bootchart.cpp",
"builtins.cpp",
"capabilities.cpp",
@@ -99,6 +102,7 @@
"devices.cpp",
"epoll.cpp",
"firmware_handler.cpp",
+ "first_stage_init.cpp",
"first_stage_mount.cpp",
"import_parser.cpp",
"init.cpp",
@@ -117,6 +121,7 @@
"sigchld_handler.cpp",
"subcontext.cpp",
"subcontext.proto",
+ "switch_root.cpp",
"rlimit_parser.cpp",
"tokenizer.cpp",
"uevent_listener.cpp",
diff --git a/init/Android.mk b/init/Android.mk
index ee030c7..700c81e 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -39,12 +39,15 @@
# --
+# Do not build this even with mmma if we're system-as-root, otherwise it will overwrite the symlink.
+ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
include $(CLEAR_VARS)
LOCAL_CPPFLAGS := $(init_cflags)
LOCAL_SRC_FILES := \
devices.cpp \
+ first_stage_init.cpp \
+ first_stage_main.cpp \
first_stage_mount.cpp \
- init_first_stage.cpp \
reboot_utils.cpp \
selinux.cpp \
switch_root.cpp \
@@ -92,21 +95,17 @@
LOCAL_SANITIZE := signed-integer-overflow
# First stage init is weird: it may start without stdout/stderr, and no /proc.
LOCAL_NOSANITIZE := hwaddress
-LOCAL_XOM := false
include $(BUILD_EXECUTABLE)
+endif
include $(CLEAR_VARS)
LOCAL_MODULE := init_system
+LOCAL_REQUIRED_MODULES := \
+ init_second_stage \
+
ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
-LOCAL_REQUIRED_MODULES := \
- init_first_stage \
- init_second_stage \
-
-else
-LOCAL_REQUIRED_MODULES := \
- init_second_stage \
-
+LOCAL_POST_INSTALL_CMD := ln -sf /system/bin/init $(TARGET_ROOT_OUT)/init
endif
include $(BUILD_PHONY_PACKAGE)
@@ -119,5 +118,3 @@
endif
include $(BUILD_PHONY_PACKAGE)
-
-
diff --git a/init/boringssl_self_test.cpp b/init/boringssl_self_test.cpp
new file mode 100644
index 0000000..0408d30
--- /dev/null
+++ b/init/boringssl_self_test.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2018 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 "boringssl_self_test.h"
+
+#include <android-base/logging.h>
+#include <cutils/android_reboot.h>
+#include <openssl/crypto.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace android {
+namespace init {
+
+Result<Success> StartBoringSslSelfTest(const BuiltinArguments&) {
+ pid_t id = fork();
+
+ if (id == 0) {
+ if (BORINGSSL_self_test() != 1) {
+ LOG(INFO) << "BoringSSL crypto self tests failed";
+
+ // This check has failed, so the device should refuse
+ // to boot. Rebooting to bootloader to wait for
+ // further action from the user.
+
+ int result = android_reboot(ANDROID_RB_RESTART2, 0,
+ "bootloader,boringssl-self-check-failed");
+ if (result != 0) {
+ LOG(ERROR) << "Failed to reboot into bootloader";
+ }
+ }
+
+ _exit(0);
+ } else if (id == -1) {
+ // Failed to fork, so cannot run the test. Refuse to continue.
+ PLOG(FATAL) << "Failed to fork for BoringSSL self test";
+ }
+
+ return Success();
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/boringssl_self_test.h b/init/boringssl_self_test.h
new file mode 100644
index 0000000..b21fc78
--- /dev/null
+++ b/init/boringssl_self_test.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#pragma once
+
+#include "builtin_arguments.h"
+#include "result.h"
+
+namespace android {
+namespace init {
+
+Result<Success> StartBoringSslSelfTest(const BuiltinArguments&);
+
+} // namespace init
+} // namespace android
diff --git a/init/init_first_stage.cpp b/init/first_stage_init.cpp
similarity index 97%
rename from init/init_first_stage.cpp
rename to init/first_stage_init.cpp
index c2c6868..e11d897 100644
--- a/init/init_first_stage.cpp
+++ b/init/first_stage_init.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "first_stage_init.h"
+
#include <dirent.h>
#include <fcntl.h>
#include <paths.h>
@@ -94,7 +96,7 @@
} // namespace
-int main(int argc, char** argv) {
+int FirstStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
@@ -214,7 +216,7 @@
setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
const char* path = "/system/bin/init";
- const char* args[] = {path, nullptr};
+ const char* args[] = {path, "selinux_setup", nullptr};
execv(path, const_cast<char**>(args));
// execv() only returns if an error happened, in which case we
@@ -226,7 +228,3 @@
} // namespace init
} // namespace android
-
-int main(int argc, char** argv) {
- return android::init::main(argc, argv);
-}
diff --git a/init/first_stage_init.h b/init/first_stage_init.h
new file mode 100644
index 0000000..0476e44
--- /dev/null
+++ b/init/first_stage_init.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#pragma once
+
+namespace android {
+namespace init {
+
+int FirstStageMain(int argc, char** argv);
+
+} // namespace init
+} // namespace android
diff --git a/init/first_stage_main.cpp b/init/first_stage_main.cpp
new file mode 100644
index 0000000..7bae84c
--- /dev/null
+++ b/init/first_stage_main.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2018 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 "first_stage_init.h"
+
+int main(int argc, char** argv) {
+ return android::init::FirstStageMain(argc, argv);
+}
diff --git a/init/init.cpp b/init/init.cpp
index e7dbc11..dc46a82 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -49,6 +49,7 @@
#endif
#include "action_parser.h"
+#include "boringssl_self_test.h"
#include "epoll.h"
#include "first_stage_mount.h"
#include "import_parser.h"
@@ -59,13 +60,8 @@
#include "security.h"
#include "selinux.h"
#include "sigchld_handler.h"
-#include "ueventd.h"
#include "util.h"
-#if __has_feature(address_sanitizer)
-#include <sanitizer/asan_interface.h>
-#endif
-
using namespace std::chrono_literals;
using namespace std::string_literals;
@@ -79,25 +75,6 @@
namespace android {
namespace init {
-#if __has_feature(address_sanitizer)
-// Load asan.options if it exists since these are not yet in the environment.
-// Always ensure detect_container_overflow=0 as there are false positives with this check.
-// Always ensure abort_on_error=1 to ensure we reboot to bootloader for development builds.
-extern "C" const char* __asan_default_options() {
- return "include_if_exists=/system/asan.options:detect_container_overflow=0:abort_on_error=1";
-}
-
-__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) extern "C" void
-__sanitizer_report_error_summary(const char* summary) {
- LOG(ERROR) << "Main stage (error summary): " << summary;
-}
-
-__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) static void
-AsanReportCallback(const char* str) {
- LOG(ERROR) << "Main stage: " << str;
-}
-#endif
-
static int property_triggers_enabled = 0;
static char qemu[32];
@@ -622,57 +599,11 @@
});
}
-static void SetupSelinux(char** argv) {
- android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) {
- RebootSystem(ANDROID_RB_RESTART2, "bootloader");
- });
-
- // Set up SELinux, loading the SELinux policy.
- SelinuxSetupKernelLogging();
- SelinuxInitialize();
-
- // We're in the kernel domain and want to transition to the init domain. File systems that
- // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
- // but other file systems do. In particular, this is needed for ramdisks such as the
- // recovery image for A/B devices.
- if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
- PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
- }
-
- setenv("SELINUX_INITIALIZED", "true", 1);
-
- const char* path = "/system/bin/init";
- const char* args[] = {path, nullptr};
- execv(path, const_cast<char**>(args));
-
- // execv() only returns if an error happened, in which case we
- // panic and never return from this function.
- PLOG(FATAL) << "execv(\"" << path << "\") failed";
-}
-
-int main(int argc, char** argv) {
-#if __has_feature(address_sanitizer)
- __asan_set_error_report_callback(AsanReportCallback);
-#endif
-
- if (!strcmp(basename(argv[0]), "ueventd")) {
- return ueventd_main(argc, argv);
- }
-
- if (argc > 1 && !strcmp(argv[1], "subcontext")) {
- android::base::InitLogging(argv, &android::base::KernelLogger);
- const BuiltinFunctionMap function_map;
- return SubcontextMain(argc, argv, &function_map);
- }
-
+int SecondStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
- if (getenv("SELINUX_INITIALIZED") == nullptr) {
- SetupSelinux(argv);
- }
-
// We need to set up stdin/stdout/stderr again now that we're running in init's context.
InitKernelLogging(argv, InitAborter);
LOG(INFO) << "init second stage started!";
@@ -708,7 +639,6 @@
if (avb_version) property_set("ro.boot.avb_version", avb_version);
// Clean up our environment.
- unsetenv("SELINUX_INITIALIZED");
unsetenv("INIT_STARTED_AT");
unsetenv("INIT_SELINUX_TOOK");
unsetenv("INIT_AVB_VERSION");
@@ -768,6 +698,9 @@
// Trigger all the boot actions to get us started.
am.QueueEventTrigger("init");
+ // Starting the BoringSSL self test, for NIAP certification compliance.
+ am.QueueBuiltinAction(StartBoringSslSelfTest, "StartBoringSslSelfTest");
+
// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
diff --git a/init/init.h b/init/init.h
index 65405aa..a76da20 100644
--- a/init/init.h
+++ b/init/init.h
@@ -50,7 +50,7 @@
void ResetWaitForProp();
-int main(int argc, char** argv);
+int SecondStageMain(int argc, char** argv);
} // namespace init
} // namespace android
diff --git a/init/main.cpp b/init/main.cpp
index 9ed451b..868c409 100644
--- a/init/main.cpp
+++ b/init/main.cpp
@@ -14,8 +14,70 @@
* limitations under the License.
*/
+#include "builtins.h"
+#include "first_stage_init.h"
#include "init.h"
+#include "selinux.h"
+#include "subcontext.h"
+#include "ueventd.h"
+
+#include <android-base/logging.h>
+
+#if __has_feature(address_sanitizer)
+#include <sanitizer/asan_interface.h>
+#endif
+
+#if __has_feature(address_sanitizer)
+// Load asan.options if it exists since these are not yet in the environment.
+// Always ensure detect_container_overflow=0 as there are false positives with this check.
+// Always ensure abort_on_error=1 to ensure we reboot to bootloader for development builds.
+extern "C" const char* __asan_default_options() {
+ return "include_if_exists=/system/asan.options:detect_container_overflow=0:abort_on_error=1";
+}
+
+__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) extern "C" void
+__sanitizer_report_error_summary(const char* summary) {
+ LOG(ERROR) << "Init (error summary): " << summary;
+}
+
+__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) static void
+AsanReportCallback(const char* str) {
+ LOG(ERROR) << "Init: " << str;
+}
+#endif
+
+using namespace android::init;
int main(int argc, char** argv) {
- android::init::main(argc, argv);
+#if __has_feature(address_sanitizer)
+ __asan_set_error_report_callback(AsanReportCallback);
+#endif
+
+ if (!strcmp(basename(argv[0]), "ueventd")) {
+ return ueventd_main(argc, argv);
+ }
+
+ if (argc < 2) {
+ return FirstStageMain(argc, argv);
+ }
+
+ if (!strcmp(argv[1], "subcontext")) {
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+ const BuiltinFunctionMap function_map;
+
+ return SubcontextMain(argc, argv, &function_map);
+ }
+
+ if (!strcmp(argv[1], "selinux_setup")) {
+ return SetupSelinux(argv);
+ }
+
+ if (!strcmp(argv[1], "second_stage")) {
+ return SecondStageMain(argc, argv);
+ }
+
+ android::base::InitLogging(argv, &android::base::KernelLogger);
+
+ LOG(ERROR) << "Unknown argument passed to init '" << argv[1] << "'";
+ return 1;
}
diff --git a/init/selinux.cpp b/init/selinux.cpp
index fd7e86f..3a09096 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -18,8 +18,8 @@
// for SELinux operation for init.
// When the system boots, there is no SEPolicy present and init is running in the kernel domain.
-// Init loads the SEPolicy from the file system, restores the context of /init based on this
-// SEPolicy, and finally exec()'s itself to run in the proper domain.
+// Init loads the SEPolicy from the file system, restores the context of /system/bin/init based on
+// this SEPolicy, and finally exec()'s itself to run in the proper domain.
// The SEPolicy on Android comes in two variants: monolithic and split.
@@ -58,8 +58,10 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/unique_fd.h>
+#include <cutils/android_reboot.h>
#include <selinux/android.h>
+#include "reboot_utils.h"
#include "util.h"
using android::base::ParseInt;
@@ -379,8 +381,6 @@
return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy();
}
-} // namespace
-
void SelinuxInitialize() {
Timer t;
@@ -405,6 +405,8 @@
setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
}
+} // namespace
+
// The files and directories that were created before initial sepolicy load or
// files on ramdisk need to have their security context restored to the proper
// value. This must happen before /dev is populated by ueventd.
@@ -496,6 +498,39 @@
return major_version;
}
+// This function initializes SELinux then execs init to run in the init SELinux context.
+int SetupSelinux(char** argv) {
+ android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) {
+ RebootSystem(ANDROID_RB_RESTART2, "bootloader");
+ });
+
+ if (REBOOT_BOOTLOADER_ON_PANIC) {
+ InstallRebootSignalHandlers();
+ }
+
+ // Set up SELinux, loading the SELinux policy.
+ SelinuxSetupKernelLogging();
+ SelinuxInitialize();
+
+ // We're in the kernel domain and want to transition to the init domain. File systems that
+ // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
+ // but other file systems do. In particular, this is needed for ramdisks such as the
+ // recovery image for A/B devices.
+ if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
+ PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
+ }
+
+ const char* path = "/system/bin/init";
+ const char* args[] = {path, "second_stage", nullptr};
+ execv(path, const_cast<char**>(args));
+
+ // execv() only returns if an error happened, in which case we
+ // panic and never return from this function.
+ PLOG(FATAL) << "execv(\"" << path << "\") failed";
+
+ return 1;
+}
+
// selinux_android_file_context_handle() takes on the order of 10+ms to run, so we want to cache
// its value. selinux_android_restorecon() also needs an sehandle for file context look up. It
// will create and store its own copy, but selinux_android_set_sehandle() can be used to provide
diff --git a/init/selinux.h b/init/selinux.h
index c41d7f0..3aa9406 100644
--- a/init/selinux.h
+++ b/init/selinux.h
@@ -23,7 +23,7 @@
namespace android {
namespace init {
-void SelinuxInitialize();
+int SetupSelinux(char** argv);
void SelinuxRestoreContext();
void SelinuxSetupKernelLogging();
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 14f82c7..a5e0dc9 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -184,6 +184,7 @@
"tests/MapInfoGetLoadBiasTest.cpp",
"tests/MapsTest.cpp",
"tests/MemoryBufferTest.cpp",
+ "tests/MemoryCacheTest.cpp",
"tests/MemoryFake.cpp",
"tests/MemoryFileTest.cpp",
"tests/MemoryLocalTest.cpp",
@@ -310,6 +311,28 @@
],
}
+//-------------------------------------------------------------------------
+// Benchmarks
+//-------------------------------------------------------------------------
+cc_benchmark {
+ name: "unwind_benchmarks",
+ host_supported: true,
+ defaults: ["libunwindstack_flags"],
+
+ // Disable optimizations so that all of the calls are not optimized away.
+ cflags: [
+ "-O0",
+ ],
+
+ srcs: [
+ "benchmarks/unwind_benchmarks.cpp",
+ ],
+
+ shared_libs: [
+ "libunwindstack",
+ ],
+}
+
// Generates the elf data for use in the tests for .gnu_debugdata frames.
// Once these files are generated, use the xz command to compress the data.
cc_binary_host {
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index cfa8c6d..a30d65e 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -174,6 +174,13 @@
return std::shared_ptr<Memory>(new MemoryRemote(pid));
}
+std::shared_ptr<Memory> Memory::CreateProcessMemoryCached(pid_t pid) {
+ if (pid == getpid()) {
+ return std::shared_ptr<Memory>(new MemoryCache(new MemoryLocal()));
+ }
+ return std::shared_ptr<Memory>(new MemoryCache(new MemoryRemote(pid)));
+}
+
size_t MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
if (addr >= raw_.size()) {
return 0;
@@ -398,4 +405,50 @@
return 0;
}
+size_t MemoryCache::Read(uint64_t addr, void* dst, size_t size) {
+ // Only bother caching and looking at the cache if this is a small read for now.
+ if (size > 64) {
+ return impl_->Read(addr, dst, size);
+ }
+
+ uint64_t addr_page = addr >> kCacheBits;
+ auto entry = cache_.find(addr_page);
+ uint8_t* cache_dst;
+ if (entry != cache_.end()) {
+ cache_dst = entry->second;
+ } else {
+ cache_dst = cache_[addr_page];
+ if (!impl_->ReadFully(addr_page << kCacheBits, cache_dst, kCacheSize)) {
+ // Erase the entry.
+ cache_.erase(addr_page);
+ return impl_->Read(addr, dst, size);
+ }
+ }
+ size_t max_read = ((addr_page + 1) << kCacheBits) - addr;
+ if (size <= max_read) {
+ memcpy(dst, &cache_dst[addr & kCacheMask], size);
+ return size;
+ }
+
+ // The read crossed into another cached entry, since a read can only cross
+ // into one extra cached page, duplicate the code rather than looping.
+ memcpy(dst, &cache_dst[addr & kCacheMask], max_read);
+ dst = &reinterpret_cast<uint8_t*>(dst)[max_read];
+ addr_page++;
+
+ entry = cache_.find(addr_page);
+ if (entry != cache_.end()) {
+ cache_dst = entry->second;
+ } else {
+ cache_dst = cache_[addr_page];
+ if (!impl_->ReadFully(addr_page << kCacheBits, cache_dst, kCacheSize)) {
+ // Erase the entry.
+ cache_.erase(addr_page);
+ return impl_->Read(addr_page << kCacheBits, dst, size - max_read) + max_read;
+ }
+ }
+ memcpy(dst, cache_dst, size - max_read);
+ return size;
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/benchmarks/unwind_benchmarks.cpp b/libunwindstack/benchmarks/unwind_benchmarks.cpp
new file mode 100644
index 0000000..db0fb54
--- /dev/null
+++ b/libunwindstack/benchmarks/unwind_benchmarks.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 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 <stdint.h>
+
+#include <memory>
+
+#include <benchmark/benchmark.h>
+
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
+#include <unwindstack/Unwinder.h>
+
+size_t Call6(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+ std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
+ unwindstack::RegsGetLocal(regs.get());
+ unwindstack::Unwinder unwinder(32, maps, regs.get(), process_memory);
+ unwinder.Unwind();
+ return unwinder.NumFrames();
+}
+
+size_t Call5(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+ return Call6(process_memory, maps);
+}
+
+size_t Call4(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+ return Call5(process_memory, maps);
+}
+
+size_t Call3(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+ return Call4(process_memory, maps);
+}
+
+size_t Call2(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+ return Call3(process_memory, maps);
+}
+
+size_t Call1(std::shared_ptr<unwindstack::Memory>& process_memory, unwindstack::Maps* maps) {
+ return Call2(process_memory, maps);
+}
+
+static void BM_uncached_unwind(benchmark::State& state) {
+ auto process_memory = unwindstack::Memory::CreateProcessMemory(getpid());
+ unwindstack::LocalMaps maps;
+ if (!maps.Parse()) {
+ state.SkipWithError("Failed to parse local maps.");
+ }
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(Call1(process_memory, &maps));
+ }
+}
+BENCHMARK(BM_uncached_unwind);
+
+static void BM_cached_unwind(benchmark::State& state) {
+ auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());
+ unwindstack::LocalMaps maps;
+ if (!maps.Parse()) {
+ state.SkipWithError("Failed to parse local maps.");
+ }
+
+ for (auto _ : state) {
+ benchmark::DoNotOptimize(Call1(process_memory, &maps));
+ }
+}
+BENCHMARK(BM_cached_unwind);
+
+BENCHMARK_MAIN();
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index 9c425cb..dba41d1 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -25,6 +25,7 @@
#include <map>
#include <memory>
#include <string>
+#include <unordered_map>
#include <vector>
namespace unwindstack {
@@ -35,9 +36,12 @@
virtual ~Memory() = default;
static std::shared_ptr<Memory> CreateProcessMemory(pid_t pid);
+ static std::shared_ptr<Memory> CreateProcessMemoryCached(pid_t pid);
virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
+ virtual void Clear() {}
+
virtual size_t Read(uint64_t addr, void* dst, size_t size) = 0;
bool ReadFully(uint64_t addr, void* dst, size_t size);
@@ -51,6 +55,24 @@
}
};
+class MemoryCache : public Memory {
+ public:
+ MemoryCache(Memory* memory) : impl_(memory) {}
+ virtual ~MemoryCache() = default;
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ void Clear() override { cache_.clear(); }
+
+ private:
+ constexpr static size_t kCacheBits = 12;
+ constexpr static size_t kCacheMask = (1 << kCacheBits) - 1;
+ constexpr static size_t kCacheSize = 1 << kCacheBits;
+ std::unordered_map<uint64_t, uint8_t[kCacheSize]> cache_;
+
+ std::unique_ptr<Memory> impl_;
+};
+
class MemoryBuffer : public Memory {
public:
MemoryBuffer() = default;
diff --git a/libunwindstack/tests/MemoryCacheTest.cpp b/libunwindstack/tests/MemoryCacheTest.cpp
new file mode 100644
index 0000000..a3def20
--- /dev/null
+++ b/libunwindstack/tests/MemoryCacheTest.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 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 <stdint.h>
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Memory.h>
+
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class MemoryCacheTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_ = new MemoryFake;
+ memory_cache_.reset(new MemoryCache(memory_));
+
+ memory_->SetMemoryBlock(0x8000, 4096, 0xab);
+ memory_->SetMemoryBlock(0x9000, 4096, 0xde);
+ memory_->SetMemoryBlock(0xa000, 3000, 0x50);
+ }
+
+ MemoryFake* memory_;
+ std::unique_ptr<MemoryCache> memory_cache_;
+
+ constexpr static size_t kMaxCachedSize = 64;
+};
+
+TEST_F(MemoryCacheTest, cached_read) {
+ for (size_t i = 1; i <= kMaxCachedSize; i++) {
+ std::vector<uint8_t> buffer(i);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+ << "Read failed at size " << i;
+ ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+ }
+
+ // Verify the cached data is used.
+ memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+ for (size_t i = 1; i <= kMaxCachedSize; i++) {
+ std::vector<uint8_t> buffer(i);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+ << "Read failed at size " << i;
+ ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+ }
+}
+
+TEST_F(MemoryCacheTest, no_cached_read_after_clear) {
+ for (size_t i = 1; i <= kMaxCachedSize; i++) {
+ std::vector<uint8_t> buffer(i);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+ << "Read failed at size " << i;
+ ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+ }
+
+ // Verify the cached data is not used after a reset.
+ memory_cache_->Clear();
+ memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+ for (size_t i = 1; i <= kMaxCachedSize; i++) {
+ std::vector<uint8_t> buffer(i);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+ << "Read failed at size " << i;
+ ASSERT_EQ(std::vector<uint8_t>(i, 0xff), buffer) << "Failed at size " << i;
+ }
+}
+
+TEST_F(MemoryCacheTest, cached_read_across_caches) {
+ std::vector<uint8_t> expect(16, 0xab);
+ expect.resize(32, 0xde);
+
+ std::vector<uint8_t> buffer(32);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8ff0, buffer.data(), 32));
+ ASSERT_EQ(expect, buffer);
+
+ // Verify the cached data is used.
+ memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+ memory_->SetMemoryBlock(0x9000, 4096, 0xff);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8ff0, buffer.data(), 32));
+ ASSERT_EQ(expect, buffer);
+}
+
+TEST_F(MemoryCacheTest, no_cache_read) {
+ for (size_t i = kMaxCachedSize + 1; i < 2 * kMaxCachedSize; i++) {
+ std::vector<uint8_t> buffer(i);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+ << "Read failed at size " << i;
+ ASSERT_EQ(std::vector<uint8_t>(i, 0xab), buffer) << "Failed at size " << i;
+ }
+
+ // Verify the cached data is not used.
+ memory_->SetMemoryBlock(0x8000, 4096, 0xff);
+ for (size_t i = kMaxCachedSize + 1; i < 2 * kMaxCachedSize; i++) {
+ std::vector<uint8_t> buffer(i);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x8000 + i, buffer.data(), i))
+ << "Read failed at size " << i;
+ ASSERT_EQ(std::vector<uint8_t>(i, 0xff), buffer) << "Failed at size " << i;
+ }
+}
+
+TEST_F(MemoryCacheTest, read_for_cache_fail) {
+ std::vector<uint8_t> buffer(kMaxCachedSize);
+ ASSERT_TRUE(memory_cache_->ReadFully(0xa010, buffer.data(), kMaxCachedSize));
+ ASSERT_EQ(std::vector<uint8_t>(kMaxCachedSize, 0x50), buffer);
+
+ // Verify the cached data is not used.
+ memory_->SetMemoryBlock(0xa000, 3000, 0xff);
+ ASSERT_TRUE(memory_cache_->ReadFully(0xa010, buffer.data(), kMaxCachedSize));
+ ASSERT_EQ(std::vector<uint8_t>(kMaxCachedSize, 0xff), buffer);
+}
+
+TEST_F(MemoryCacheTest, read_for_cache_fail_cross) {
+ std::vector<uint8_t> expect(16, 0xde);
+ expect.resize(32, 0x50);
+
+ std::vector<uint8_t> buffer(32);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x9ff0, buffer.data(), 32));
+ ASSERT_EQ(expect, buffer);
+
+ // Verify the cached data is not used for the second half but for the first.
+ memory_->SetMemoryBlock(0xa000, 3000, 0xff);
+ ASSERT_TRUE(memory_cache_->ReadFully(0x9ff0, buffer.data(), 32));
+ expect.resize(16);
+ expect.resize(32, 0xff);
+ ASSERT_EQ(expect, buffer);
+}
+
+} // namespace unwindstack