Merge "Set SPI Allocation Timeout to One Hour"
diff --git a/OWNERS b/OWNERS
index 682a067..1d319af 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1 +1,5 @@
enh@google.com
+per-file libsysutils/src/Netlink* = ek@google.com
+per-file libsysutils/src/Netlink* = lorenzo@google.com
+per-file libsysutils/include/sysutils/Netlink* = ek@google.com
+per-file libsysutils/include/sysutils/Netlink* = lorenzo@google.com
diff --git a/adb/Android.bp b/adb/Android.bp
index 0858a6c..bbf7cb4 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -12,6 +12,325 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+cc_defaults {
+ name: "adb_defaults",
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ "-Wno-unused-parameter",
+ "-Wno-missing-field-initializers",
+ "-Wvla",
+ ],
+ rtti: true,
+
+ clang_cflags: [
+ "-Wexit-time-destructors",
+ "-Wthread-safety",
+ ],
+
+ use_version_lib: true,
+
+ compile_multilib: "first",
+ product_variables: {
+ debuggable: {
+ cflags: [
+ "-DALLOW_ADBD_ROOT",
+ "-DALLOW_ADBD_DISABLE_VERITY",
+ "-DALLOW_ADBD_NO_AUTH",
+ ],
+ },
+ },
+
+ target: {
+ android: {
+ cflags: ["-DADB_HOST=0"],
+ },
+
+ host: {
+ cflags: ["-DADB_HOST=1"],
+ },
+
+ darwin: {
+ host_ldlibs: [
+ "-lpthread",
+ "-framework CoreFoundation",
+ "-framework IOKit",
+ "-lobjc",
+ ],
+ },
+
+ windows: {
+ cflags: [
+ // Define windows.h and tchar.h Unicode preprocessor symbols so that
+ // CreateFile(), _tfopen(), etc. map to versions that take wchar_t*, breaking the
+ // build if you accidentally pass char*. Fix by calling like:
+ // std::wstring path_wide;
+ // if (!android::base::UTF8ToWide(path_utf8, &path_wide)) { /* error handling */ }
+ // CreateFileW(path_wide.c_str());
+ "-DUNICODE=1",
+ "-D_UNICODE=1",
+
+ // -std=gnu++14 doesn't set _GNU_SOURCE on Windows.
+ "-D_GNU_SOURCE",
+ ],
+ },
+ },
+}
+
+// libadb
+// =========================================================
+// These files are compiled for both the host and the device.
+libadb_srcs = [
+ "adb.cpp",
+ "adb_io.cpp",
+ "adb_listeners.cpp",
+ "adb_trace.cpp",
+ "adb_utils.cpp",
+ "fdevent.cpp",
+ "services.cpp",
+ "sockets.cpp",
+ "socket_spec.cpp",
+ "sysdeps/errno.cpp",
+ "transport.cpp",
+ "transport_local.cpp",
+ "transport_usb.cpp",
+]
+
+libadb_posix_srcs = [
+ "sysdeps_unix.cpp",
+ "sysdeps/posix/network.cpp",
+]
+
+libadb_test_srcs = [
+ "adb_io_test.cpp",
+ "adb_listeners_test.cpp",
+ "adb_utils_test.cpp",
+ "fdevent_test.cpp",
+ "socket_spec_test.cpp",
+ "socket_test.cpp",
+ "sysdeps_test.cpp",
+ "sysdeps/stat_test.cpp",
+ "transport_test.cpp",
+]
+
+cc_library_host_static {
+ name: "libadb_host",
+ defaults: ["adb_defaults"],
+
+ srcs: libadb_srcs + [
+ "client/auth.cpp",
+ "client/usb_libusb.cpp",
+ "client/usb_dispatch.cpp",
+ "client/transport_mdns.cpp",
+ ],
+
+ target: {
+ linux: {
+ srcs: ["client/usb_linux.cpp"],
+ },
+ darwin: {
+ srcs: ["client/usb_osx.cpp"],
+ },
+
+ not_windows: {
+ srcs: libadb_posix_srcs,
+ },
+ windows: {
+ enabled: true,
+ srcs: [
+ "client/usb_windows.cpp",
+ "sysdeps_win32.cpp",
+ "sysdeps/win32/errno.cpp",
+ "sysdeps/win32/stat.cpp",
+ ],
+ shared_libs: ["AdbWinApi"],
+ },
+ },
+
+ static_libs: [
+ "libbase",
+ "libcrypto_utils",
+ "libcrypto",
+ "libdiagnose_usb",
+ "libmdnssd",
+ "libusb",
+ ],
+}
+
+cc_test_host {
+ name: "adb_test",
+ defaults: ["adb_defaults"],
+ srcs: libadb_test_srcs,
+ static_libs: [
+ "libadb_host",
+ "libbase",
+ "libcutils",
+ "libcrypto_utils",
+ "libcrypto",
+ "libmdnssd",
+ "libdiagnose_usb",
+ "libusb",
+ ],
+}
+
+cc_binary_host {
+ name: "adb",
+ tags: ["debug"],
+
+ defaults: ["adb_defaults"],
+
+ srcs: [
+ "client/adb_client.cpp",
+ "client/bugreport.cpp",
+ "client/commandline.cpp",
+ "client/file_sync_client.cpp",
+ "client/main.cpp",
+ "client/console.cpp",
+ "client/line_printer.cpp",
+ "shell_service_protocol.cpp",
+ ],
+
+ static_libs: [
+ "libadb_host",
+ "libbase",
+ "libcutils",
+ "libcrypto_utils",
+ "libcrypto",
+ "libdiagnose_usb",
+ "liblog",
+ "libmdnssd",
+ "libusb",
+ ],
+
+ stl: "libc++_static",
+
+ // Don't add anything here, we don't want additional shared dependencies
+ // on the host adb tool, and shared libraries that link against libc++
+ // will violate ODR
+ shared_libs: [],
+
+ target: {
+ darwin: {
+ cflags: [
+ "-Wno-sizeof-pointer-memaccess",
+ ],
+ },
+ windows: {
+ enabled: true,
+ ldflags: ["-municode"],
+ host_ldlibs: [
+ "-lws2_32",
+ "-lgdi32",
+ ],
+
+ shared_libs: ["AdbWinApi"],
+ required: [
+ "AdbWinUsbApi",
+ ],
+ },
+ },
+}
+
+cc_library_static {
+ name: "libadbd",
+ defaults: ["adb_defaults"],
+
+ // libminadbd wants both, for some reason.
+ compile_multilib: "both",
+ srcs: libadb_srcs + libadb_posix_srcs + [
+ "daemon/auth.cpp",
+ "daemon/usb.cpp",
+ "daemon/jdwp_service.cpp",
+ ],
+
+ static_libs: [
+ "libasyncio",
+ "libbootloader_message",
+ "libcrypto_utils",
+ "libcrypto",
+ "libdiagnose_usb",
+ "libqemu_pipe",
+ "libbase",
+ ],
+}
+
+cc_binary {
+ name: "adbd",
+ defaults: ["adb_defaults"],
+
+ // adbd must be static, as it is copied into the recovery image.
+ static_executable: true,
+
+ srcs: [
+ "daemon/main.cpp",
+ "daemon/mdns.cpp",
+ "daemon/file_sync_service.cpp",
+ "daemon/framebuffer_service.cpp",
+ "daemon/remount_service.cpp",
+ "daemon/set_verity_enable_state_service.cpp",
+ "daemon/shell_service.cpp",
+ "shell_service_protocol.cpp",
+ ],
+
+ cflags: [
+ "-D_GNU_SOURCE",
+ "-Wno-deprecated-declarations",
+ ],
+
+ strip: {
+ keep_symbols: true,
+ },
+
+ static_libs: [
+ "libadbd",
+ "libasyncio",
+ "libavb_user",
+ "libbootloader_message",
+ "libcrypto_utils",
+ "libcrypto",
+ "libdiagnose_usb",
+ "libfec",
+ "libfec_rs",
+ "libfs_mgr",
+ "liblog",
+ "libext4_utils",
+ "libmdnssd",
+ "libminijail",
+ "libselinux",
+ "libsquashfs_utils",
+ "libqemu_pipe",
+ "libdebuggerd_handler",
+
+ "libbase",
+ "libcutils",
+ ],
+}
+
+cc_test {
+ name: "adbd_test",
+ defaults: ["adb_defaults"],
+ srcs: libadb_test_srcs + [
+ "daemon/shell_service.cpp",
+ "daemon/shell_service_test.cpp",
+ "shell_service_protocol.cpp",
+ "shell_service_protocol_test.cpp",
+ ],
+
+ static_libs: [
+ "libadbd",
+ "libbase",
+ "libcutils",
+ "libcrypto_utils",
+ "libcrypto",
+ "libdiagnose_usb",
+ "liblog",
+ "libusb",
+ "libmdnssd",
+ ],
+}
+
python_binary_host {
name: "adb_integration_test_adb",
main: "test_adb.py",
diff --git a/adb/Android.mk b/adb/Android.mk
deleted file mode 100644
index e52f0cb..0000000
--- a/adb/Android.mk
+++ /dev/null
@@ -1,387 +0,0 @@
-# Copyright 2005 The Android Open Source Project
-#
-# Android.mk for adb
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(LOCAL_PATH)/../platform_tools_tool_version.mk
-
-adb_host_sanitize :=
-adb_target_sanitize :=
-
-ADB_COMMON_CFLAGS := \
- -frtti \
- -Wall -Wextra -Werror \
- -Wno-unused-parameter \
- -Wno-missing-field-initializers \
- -Wvla \
- -DADB_VERSION="\"$(tool_version)\"" \
-
-ADB_COMMON_posix_CFLAGS := \
- -Wexit-time-destructors \
- -Wthread-safety \
-
-ADB_COMMON_linux_CFLAGS := \
- $(ADB_COMMON_posix_CFLAGS) \
-
-ADB_COMMON_darwin_CFLAGS := \
- $(ADB_COMMON_posix_CFLAGS) \
-
-# Define windows.h and tchar.h Unicode preprocessor symbols so that
-# CreateFile(), _tfopen(), etc. map to versions that take wchar_t*, breaking the
-# build if you accidentally pass char*. Fix by calling like:
-# std::wstring path_wide;
-# if (!android::base::UTF8ToWide(path_utf8, &path_wide)) { /* error handling */ }
-# CreateFileW(path_wide.c_str());
-ADB_COMMON_windows_CFLAGS := \
- -DUNICODE=1 -D_UNICODE=1 \
-
-# libadb
-# =========================================================
-
-# Much of adb is duplicated in bootable/recovery/minadb and fastboot. Changes
-# made to adb rarely get ported to the other two, so the trees have diverged a
-# bit. We'd like to stop this because it is a maintenance nightmare, but the
-# divergence makes this difficult to do all at once. For now, we will start
-# small by moving common files into a static library. Hopefully some day we can
-# get enough of adb in here that we no longer need minadb. https://b/17626262
-LIBADB_SRC_FILES := \
- adb.cpp \
- adb_io.cpp \
- adb_listeners.cpp \
- adb_trace.cpp \
- adb_utils.cpp \
- fdevent.cpp \
- sockets.cpp \
- socket_spec.cpp \
- sysdeps/errno.cpp \
- transport.cpp \
- transport_local.cpp \
- transport_usb.cpp \
-
-LIBADB_TEST_SRCS := \
- adb_io_test.cpp \
- adb_listeners_test.cpp \
- adb_utils_test.cpp \
- fdevent_test.cpp \
- socket_spec_test.cpp \
- socket_test.cpp \
- sysdeps_test.cpp \
- sysdeps/stat_test.cpp \
- transport_test.cpp \
-
-LIBADB_CFLAGS := \
- $(ADB_COMMON_CFLAGS) \
- -fvisibility=hidden \
-
-LIBADB_linux_CFLAGS := \
- $(ADB_COMMON_linux_CFLAGS) \
-
-LIBADB_darwin_CFLAGS := \
- $(ADB_COMMON_darwin_CFLAGS) \
-
-LIBADB_windows_CFLAGS := \
- $(ADB_COMMON_windows_CFLAGS) \
-
-LIBADB_darwin_SRC_FILES := \
- sysdeps_unix.cpp \
- sysdeps/posix/network.cpp \
- client/usb_dispatch.cpp \
- client/usb_libusb.cpp \
- client/usb_osx.cpp \
-
-LIBADB_linux_SRC_FILES := \
- sysdeps_unix.cpp \
- sysdeps/posix/network.cpp \
- client/usb_dispatch.cpp \
- client/usb_libusb.cpp \
- client/usb_linux.cpp \
-
-LIBADB_windows_SRC_FILES := \
- sysdeps_win32.cpp \
- sysdeps/win32/errno.cpp \
- sysdeps/win32/stat.cpp \
- client/usb_dispatch.cpp \
- client/usb_libusb.cpp \
- client/usb_windows.cpp \
-
-LIBADB_TEST_windows_SRCS := \
- sysdeps/win32/errno_test.cpp \
- sysdeps_win32_test.cpp \
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libadbd_usb
-LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=0
-LOCAL_SRC_FILES := daemon/usb.cpp
-
-LOCAL_SANITIZE := $(adb_target_sanitize)
-
-# Even though we're building a static library (and thus there's no link step for
-# this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase libasyncio
-
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libadbd
-LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=0
-LOCAL_SRC_FILES := \
- $(LIBADB_SRC_FILES) \
- adbd_auth.cpp \
- jdwp_service.cpp \
- sysdeps/posix/network.cpp \
-
-LOCAL_SANITIZE := $(adb_target_sanitize)
-
-# Even though we're building a static library (and thus there's no link step for
-# this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libqemu_pipe libbase
-
-LOCAL_WHOLE_STATIC_LIBRARIES := libadbd_usb
-
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libadb
-LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=1
-LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
-LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
-LOCAL_CFLAGS_darwin := $(LIBADB_darwin_CFLAGS)
-LOCAL_SRC_FILES := \
- $(LIBADB_SRC_FILES) \
- adb_auth_host.cpp \
- transport_mdns.cpp \
-
-LOCAL_SRC_FILES_darwin := $(LIBADB_darwin_SRC_FILES)
-LOCAL_SRC_FILES_linux := $(LIBADB_linux_SRC_FILES)
-LOCAL_SRC_FILES_windows := $(LIBADB_windows_SRC_FILES)
-
-LOCAL_SANITIZE := $(adb_host_sanitize)
-
-# Even though we're building a static library (and thus there's no link step for
-# this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase libmdnssd libusb
-
-LOCAL_C_INCLUDES_windows := development/host/windows/usb/api/
-LOCAL_MULTILIB := first
-
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := adbd_test
-LOCAL_CFLAGS := -DADB_HOST=0 $(LIBADB_CFLAGS)
-LOCAL_SRC_FILES := \
- $(LIBADB_TEST_SRCS) \
- $(LIBADB_TEST_linux_SRCS) \
- shell_service.cpp \
- shell_service_protocol.cpp \
- shell_service_protocol_test.cpp \
- shell_service_test.cpp \
-
-LOCAL_SANITIZE := $(adb_target_sanitize)
-LOCAL_STATIC_LIBRARIES := libadbd libcrypto_utils libcrypto libusb libmdnssd
-LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
-include $(BUILD_NATIVE_TEST)
-
-# libdiagnose_usb
-# =========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libdiagnose_usb
-LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_CFLAGS := $(LIBADB_CFLAGS)
-LOCAL_SRC_FILES := diagnose_usb.cpp
-# Even though we're building a static library (and thus there's no link step for
-# this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libbase
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-# adb_test
-# =========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := adb_test
-LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
-LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
-LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
-LOCAL_CFLAGS_darwin := $(LIBADB_darwin_CFLAGS)
-LOCAL_SRC_FILES := \
- $(LIBADB_TEST_SRCS) \
- adb_client.cpp \
- bugreport.cpp \
- bugreport_test.cpp \
- line_printer.cpp \
- services.cpp \
- shell_service_protocol.cpp \
- shell_service_protocol_test.cpp \
-
-LOCAL_SRC_FILES_linux := $(LIBADB_TEST_linux_SRCS)
-LOCAL_SRC_FILES_darwin := $(LIBADB_TEST_darwin_SRCS)
-LOCAL_SRC_FILES_windows := $(LIBADB_TEST_windows_SRCS)
-LOCAL_SANITIZE := $(adb_host_sanitize)
-LOCAL_STATIC_LIBRARIES := \
- libadb \
- libbase \
- libcrypto_utils \
- libcrypto \
- libcutils \
- libdiagnose_usb \
- libmdnssd \
- libgmock_host \
- libusb \
-
-# Set entrypoint to wmain from sysdeps_win32.cpp instead of main
-LOCAL_LDFLAGS_windows := -municode
-LOCAL_LDLIBS_linux := -lrt -ldl -lpthread
-LOCAL_LDLIBS_darwin := -framework CoreFoundation -framework IOKit -lobjc
-LOCAL_LDLIBS_windows := -lws2_32 -luserenv
-LOCAL_SHARED_LIBRARIES_windows := AdbWinApi
-
-LOCAL_MULTILIB := first
-
-include $(BUILD_HOST_NATIVE_TEST)
-
-# adb host tool
-# =========================================================
-include $(CLEAR_VARS)
-
-LOCAL_LDLIBS_linux := -lrt -ldl -lpthread
-
-LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon -lobjc
-
-# Use wmain instead of main
-LOCAL_LDFLAGS_windows := -municode
-LOCAL_LDLIBS_windows := -lws2_32 -lgdi32
-LOCAL_SHARED_LIBRARIES_windows := AdbWinApi
-LOCAL_REQUIRED_MODULES_windows := AdbWinUsbApi
-
-LOCAL_SRC_FILES := \
- adb_client.cpp \
- bugreport.cpp \
- client/main.cpp \
- console.cpp \
- commandline.cpp \
- file_sync_client.cpp \
- line_printer.cpp \
- services.cpp \
- shell_service_protocol.cpp \
-
-LOCAL_CFLAGS += \
- $(ADB_COMMON_CFLAGS) \
- -D_GNU_SOURCE \
- -DADB_HOST=1 \
-
-LOCAL_CFLAGS_windows := \
- $(ADB_COMMON_windows_CFLAGS)
-
-LOCAL_CFLAGS_linux := \
- $(ADB_COMMON_linux_CFLAGS) \
-
-LOCAL_CFLAGS_darwin := \
- $(ADB_COMMON_darwin_CFLAGS) \
- -Wno-sizeof-pointer-memaccess -Wno-unused-parameter \
-
-LOCAL_MODULE := adb
-LOCAL_MODULE_TAGS := debug
-LOCAL_MODULE_HOST_OS := darwin linux windows
-
-LOCAL_SANITIZE := $(adb_host_sanitize)
-LOCAL_STATIC_LIBRARIES := \
- libadb \
- libbase \
- libcrypto_utils \
- libcrypto \
- libdiagnose_usb \
- liblog \
- libmdnssd \
- libusb \
-
-# Don't use libcutils on Windows.
-LOCAL_STATIC_LIBRARIES_darwin := libcutils
-LOCAL_STATIC_LIBRARIES_linux := libcutils
-
-LOCAL_CXX_STL := libc++_static
-
-# Don't add anything here, we don't want additional shared dependencies
-# on the host adb tool, and shared libraries that link against libc++
-# will violate ODR
-LOCAL_SHARED_LIBRARIES :=
-
-include $(BUILD_HOST_EXECUTABLE)
-
-$(call dist-for-goals,dist_files sdk win_sdk,$(LOCAL_BUILT_MODULE))
-ifdef HOST_CROSS_OS
-# Archive adb.exe for win_sdk build.
-$(call dist-for-goals,win_sdk,$(ALL_MODULES.host_cross_adb.BUILT))
-endif
-
-
-# adbd device daemon
-# =========================================================
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- daemon/main.cpp \
- daemon/mdns.cpp \
- services.cpp \
- file_sync_service.cpp \
- framebuffer_service.cpp \
- remount_service.cpp \
- set_verity_enable_state_service.cpp \
- shell_service.cpp \
- shell_service_protocol.cpp \
-
-LOCAL_CFLAGS := \
- $(ADB_COMMON_CFLAGS) \
- $(ADB_COMMON_linux_CFLAGS) \
- -DADB_HOST=0 \
- -D_GNU_SOURCE \
- -Wno-deprecated-declarations \
-
-LOCAL_CFLAGS += -DALLOW_ADBD_NO_AUTH=$(if $(filter userdebug eng,$(TARGET_BUILD_VARIANT)),1,0)
-
-ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
-LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
-LOCAL_CFLAGS += -DALLOW_ADBD_ROOT=1
-endif
-
-LOCAL_MODULE := adbd
-
-LOCAL_FORCE_STATIC_EXECUTABLE := true
-
-LOCAL_SANITIZE := $(adb_target_sanitize)
-LOCAL_STRIP_MODULE := keep_symbols
-LOCAL_STATIC_LIBRARIES := \
- libadbd \
- libasyncio \
- libavb_user \
- libbase \
- libqemu_pipe \
- libbootloader_message \
- libfs_mgr \
- libfec \
- libfec_rs \
- libselinux \
- liblog \
- libext4_utils \
- libsquashfs_utils \
- libcutils \
- libbase \
- libcrypto_utils \
- libcrypto \
- libminijail \
- libmdnssd \
- libdebuggerd_handler \
-
-include $(BUILD_EXECUTABLE)
-
-# adb integration test
-# =========================================================
-$(call dist-for-goals,sdk,$(ALL_MODULES.adb_integration_test_adb.BUILT))
-$(call dist-for-goals,sdk,$(ALL_MODULES.adb_integration_test_device.BUILT))
-
-include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/adb/CPPLINT.cfg b/adb/CPPLINT.cfg
deleted file mode 100644
index f496490..0000000
--- a/adb/CPPLINT.cfg
+++ /dev/null
@@ -1,2 +0,0 @@
-set noparent
-filter=-build/header_guard,-build/include,-readability/function,-whitespace/indent
diff --git a/adb/adb.cpp b/adb/adb.cpp
index ee3503b..65fa2e7 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -42,9 +42,9 @@
#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/parsenetaddress.h>
-#include <android-base/quick_exit.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include <build/version.h>
#include "adb_auth.h"
#include "adb_io.h"
@@ -67,8 +67,8 @@
"Android Debug Bridge version %d.%d.%d\n"
"Version %s\n"
"Installed as %s\n",
- ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_VERSION,
- android::base::GetExecutablePath().c_str());
+ ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
+ android::build::GetBuildNumber().c_str(), android::base::GetExecutablePath().c_str());
}
void fatal(const char *fmt, ...) {
@@ -105,31 +105,27 @@
}
uint32_t calculate_apacket_checksum(const apacket* p) {
- const unsigned char* x = reinterpret_cast<const unsigned char*>(p->data);
uint32_t sum = 0;
- size_t count = p->msg.data_length;
-
- while (count-- > 0) {
- sum += *x++;
+ for (size_t i = 0; i < p->msg.data_length; ++i) {
+ sum += static_cast<uint8_t>(p->payload[i]);
}
-
return sum;
}
apacket* get_apacket(void)
{
- apacket* p = reinterpret_cast<apacket*>(malloc(sizeof(apacket)));
+ apacket* p = new apacket();
if (p == nullptr) {
fatal("failed to allocate an apacket");
}
- memset(p, 0, sizeof(apacket) - MAX_PAYLOAD);
+ memset(&p->msg, 0, sizeof(p->msg));
return p;
}
void put_apacket(apacket *p)
{
- free(p);
+ delete p;
}
void handle_online(atransport *t)
@@ -140,8 +136,16 @@
void handle_offline(atransport *t)
{
- D("adb: offline");
- //Close the associated usb
+ if (t->GetConnectionState() == kCsOffline) {
+ LOG(INFO) << t->serial_name() << ": already offline";
+ return;
+ }
+
+ LOG(INFO) << t->serial_name() << ": offline";
+
+ t->SetConnectionState(kCsOffline);
+
+ // Close the associated usb
t->online = 0;
// This is necessary to avoid a race condition that occurred when a transport closes
@@ -155,8 +159,7 @@
#define DUMPMAX 32
void print_packet(const char *label, apacket *p)
{
- char *tag;
- char *x;
+ const char* tag;
unsigned count;
switch(p->msg.command){
@@ -173,15 +176,15 @@
fprintf(stderr, "%s: %s %08x %08x %04x \"",
label, tag, p->msg.arg0, p->msg.arg1, p->msg.data_length);
count = p->msg.data_length;
- x = (char*) p->data;
- if(count > DUMPMAX) {
+ const char* x = p->payload.data();
+ if (count > DUMPMAX) {
count = DUMPMAX;
tag = "\n";
} else {
tag = "\"\n";
}
- while(count-- > 0){
- if((*x >= ' ') && (*x < 127)) {
+ while (count-- > 0) {
+ if ((*x >= ' ') && (*x < 127)) {
fputc(*x, stderr);
} else {
fputc('.', stderr);
@@ -254,8 +257,8 @@
<< connection_str.length() << ")";
}
- memcpy(cp->data, connection_str.c_str(), connection_str.length());
- cp->msg.data_length = connection_str.length();
+ cp->payload = std::move(connection_str);
+ cp->msg.data_length = cp->payload.size();
send_packet(cp, t);
}
@@ -323,15 +326,10 @@
}
static void handle_new_connection(atransport* t, apacket* p) {
- if (t->GetConnectionState() != kCsOffline) {
- t->SetConnectionState(kCsOffline);
- handle_offline(t);
- }
+ handle_offline(t);
t->update_version(p->msg.arg0, p->msg.arg1);
- std::string banner(reinterpret_cast<const char*>(p->data),
- p->msg.data_length);
- parse_banner(banner, t);
+ parse_banner(p->payload, t);
#if ADB_HOST
handle_online(t);
@@ -354,21 +352,9 @@
((char*) (&(p->msg.command)))[2],
((char*) (&(p->msg.command)))[3]);
print_packet("recv", p);
+ CHECK_EQ(p->payload.size(), p->msg.data_length);
switch(p->msg.command){
- case A_SYNC:
- if (p->msg.arg0){
- send_packet(p, t);
-#if ADB_HOST
- send_connect(t);
-#endif
- } else {
- t->SetConnectionState(kCsOffline);
- handle_offline(t);
- send_packet(p, t);
- }
- return;
-
case A_CNXN: // CONNECT(version, maxdata, "system-id-string")
handle_new_connection(t, p);
break;
@@ -380,11 +366,11 @@
if (t->GetConnectionState() == kCsOffline) {
t->SetConnectionState(kCsUnauthorized);
}
- send_auth_response(p->data, p->msg.data_length, t);
+ send_auth_response(p->payload.data(), p->msg.data_length, t);
break;
#else
case ADB_AUTH_SIGNATURE:
- if (adbd_auth_verify(t->token, sizeof(t->token), p->data, p->msg.data_length)) {
+ if (adbd_auth_verify(t->token, sizeof(t->token), p->payload)) {
adbd_auth_verified(t);
t->failed_auth_attempts = 0;
} else {
@@ -394,7 +380,7 @@
break;
case ADB_AUTH_RSAPUBLICKEY:
- adbd_auth_confirm_key(p->data, p->msg.data_length, t);
+ adbd_auth_confirm_key(p->payload.data(), p->msg.data_length, t);
break;
#endif
default:
@@ -406,9 +392,7 @@
case A_OPEN: /* OPEN(local-id, 0, "destination") */
if (t->online && p->msg.arg0 != 0 && p->msg.arg1 == 0) {
- char *name = (char*) p->data;
- name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0;
- asocket* s = create_local_service_socket(name, t);
+ asocket* s = create_local_service_socket(p->payload.c_str(), t);
if (s == nullptr) {
send_close(0, p->msg.arg0, t);
} else {
@@ -474,11 +458,7 @@
asocket* s = find_local_socket(p->msg.arg1, p->msg.arg0);
if (s) {
unsigned rid = p->msg.arg0;
-
- // TODO: Convert apacket::data to a type that we can move out of.
- std::string copy(p->data, p->data + p->msg.data_length);
-
- if (s->enqueue(s, std::move(copy)) == 0) {
+ if (s->enqueue(s, std::move(p->payload)) == 0) {
D("Enqueue the socket");
send_ready(s->id, rid, t);
}
@@ -1071,7 +1051,7 @@
SendOkay(reply_fd);
// Rely on process exit to close the socket for us.
- android::base::quick_exit(0);
+ exit(0);
}
// "transport:" is used for switching transport with a specified serial number
diff --git a/adb/adb.h b/adb/adb.h
index c9c635a..a6d0463 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -74,7 +74,7 @@
struct apacket {
amessage msg;
- char data[MAX_PAYLOAD];
+ std::string payload;
};
uint32_t calculate_apacket_checksum(const apacket* packet);
diff --git a/adb/adb_auth.h b/adb/adb_auth.h
index a6f224f..715e04f 100644
--- a/adb/adb_auth.h
+++ b/adb/adb_auth.h
@@ -49,7 +49,7 @@
void adbd_auth_verified(atransport *t);
void adbd_cloexec_auth_socket();
-bool adbd_auth_verify(const char* token, size_t token_size, const char* sig, int sig_len);
+bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig);
void adbd_auth_confirm_key(const char* data, size_t len, atransport* t);
void send_auth_request(atransport *t);
diff --git a/adb/adb_client.cpp b/adb/client/adb_client.cpp
similarity index 100%
rename from adb/adb_client.cpp
rename to adb/client/adb_client.cpp
diff --git a/adb/adb_client.h b/adb/client/adb_client.h
similarity index 100%
rename from adb/adb_client.h
rename to adb/client/adb_client.h
diff --git a/adb/adb_auth_host.cpp b/adb/client/auth.cpp
similarity index 95%
rename from adb/adb_auth_host.cpp
rename to adb/client/auth.cpp
index 365bf77..c3aef16 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/client/auth.cpp
@@ -299,20 +299,25 @@
return result;
}
-static int adb_auth_sign(RSA* key, const char* token, size_t token_size, char* sig) {
+static std::string adb_auth_sign(RSA* key, const char* token, size_t token_size) {
if (token_size != TOKEN_SIZE) {
D("Unexpected token size %zd", token_size);
return 0;
}
+ std::string result;
+ result.resize(MAX_PAYLOAD);
+
unsigned int len;
if (!RSA_sign(NID_sha1, reinterpret_cast<const uint8_t*>(token), token_size,
- reinterpret_cast<uint8_t*>(sig), &len, key)) {
- return 0;
+ reinterpret_cast<uint8_t*>(&result[0]), &len, key)) {
+ return std::string();
}
+ result.resize(len);
+
D("adb_auth_sign len=%d", len);
- return (int)len;
+ return result;
}
std::string adb_auth_get_userkey() {
@@ -446,13 +451,14 @@
}
apacket* p = get_apacket();
- memcpy(p->data, key.c_str(), key.size() + 1);
-
p->msg.command = A_AUTH;
p->msg.arg0 = ADB_AUTH_RSAPUBLICKEY;
+ p->payload = std::move(key);
+
// adbd expects a null-terminated string.
- p->msg.data_length = key.size() + 1;
+ p->payload.push_back('\0');
+ p->msg.data_length = p->payload.size();
send_packet(p, t);
}
@@ -467,8 +473,8 @@
LOG(INFO) << "Calling send_auth_response";
apacket* p = get_apacket();
- int ret = adb_auth_sign(key.get(), token, token_size, p->data);
- if (!ret) {
+ std::string result = adb_auth_sign(key.get(), token, token_size);
+ if (result.empty()) {
D("Error signing the token");
put_apacket(p);
return;
@@ -476,6 +482,7 @@
p->msg.command = A_AUTH;
p->msg.arg0 = ADB_AUTH_SIGNATURE;
- p->msg.data_length = ret;
+ p->payload = std::move(result);
+ p->msg.data_length = p->payload.size();
send_packet(p, t);
}
diff --git a/adb/bugreport.cpp b/adb/client/bugreport.cpp
similarity index 100%
rename from adb/bugreport.cpp
rename to adb/client/bugreport.cpp
diff --git a/adb/bugreport.h b/adb/client/bugreport.h
similarity index 100%
rename from adb/bugreport.h
rename to adb/client/bugreport.h
diff --git a/adb/commandline.cpp b/adb/client/commandline.cpp
similarity index 100%
rename from adb/commandline.cpp
rename to adb/client/commandline.cpp
diff --git a/adb/commandline.h b/adb/client/commandline.h
similarity index 100%
rename from adb/commandline.h
rename to adb/client/commandline.h
diff --git a/adb/console.cpp b/adb/client/console.cpp
similarity index 100%
rename from adb/console.cpp
rename to adb/client/console.cpp
diff --git a/adb/file_sync_client.cpp b/adb/client/file_sync_client.cpp
similarity index 100%
rename from adb/file_sync_client.cpp
rename to adb/client/file_sync_client.cpp
diff --git a/adb/line_printer.cpp b/adb/client/line_printer.cpp
similarity index 100%
rename from adb/line_printer.cpp
rename to adb/client/line_printer.cpp
diff --git a/adb/line_printer.h b/adb/client/line_printer.h
similarity index 100%
rename from adb/line_printer.h
rename to adb/client/line_printer.h
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index f0d0ce7..31cb853 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -28,7 +28,6 @@
#include <android-base/errors.h>
#include <android-base/file.h>
#include <android-base/logging.h>
-#include <android-base/quick_exit.h>
#include <android-base/stringprintf.h>
#include "adb.h"
@@ -61,7 +60,7 @@
static BOOL WINAPI ctrlc_handler(DWORD type) {
// TODO: Consider trying to kill a starting up adb server (if we're in
// launch_server) by calling GenerateConsoleCtrlEvent().
- android::base::quick_exit(STATUS_CONTROL_C_EXIT);
+ exit(STATUS_CONTROL_C_EXIT);
return TRUE;
}
#endif
@@ -76,6 +75,12 @@
usb_cleanup();
}
+static void intentionally_leak() {
+ void* p = ::operator new(1);
+ // The analyzer is upset about this leaking. NOLINTNEXTLINE
+ LOG(INFO) << "leaking pointer " << p;
+}
+
int adb_server_main(int is_daemon, const std::string& socket_spec, int ack_reply_fd) {
#if defined(_WIN32)
// adb start-server starts us up with stdout and stderr hooked up to
@@ -95,16 +100,21 @@
SetConsoleCtrlHandler(ctrlc_handler, TRUE);
#else
signal(SIGINT, [](int) {
- fdevent_run_on_main_thread([]() { android::base::quick_exit(0); });
+ fdevent_run_on_main_thread([]() { exit(0); });
});
#endif
+ char* leak = getenv("ADB_LEAK");
+ if (leak && strcmp(leak, "1") == 0) {
+ intentionally_leak();
+ }
+
if (is_daemon) {
close_stdin();
setup_daemon_logging();
}
- android::base::at_quick_exit(adb_server_cleanup);
+ atexit(adb_server_cleanup);
init_transport_registration();
init_mdns_transport_discovery();
diff --git a/adb/transport_mdns.cpp b/adb/client/transport_mdns.cpp
similarity index 100%
rename from adb/transport_mdns.cpp
rename to adb/client/transport_mdns.cpp
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 18f585d..46c3f58 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -19,6 +19,7 @@
#include "sysdeps.h"
#include <stdint.h>
+#include <stdlib.h>
#include <atomic>
#include <chrono>
@@ -33,7 +34,6 @@
#include <android-base/file.h>
#include <android-base/logging.h>
-#include <android-base/quick_exit.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
diff --git a/adb/adbd_auth.cpp b/adb/daemon/auth.cpp
similarity index 97%
rename from adb/adbd_auth.cpp
rename to adb/daemon/auth.cpp
index 3488ad1..3fd2b31 100644
--- a/adb/adbd_auth.cpp
+++ b/adb/daemon/auth.cpp
@@ -46,7 +46,7 @@
bool auth_required = true;
-bool adbd_auth_verify(const char* token, size_t token_size, const char* sig, int sig_len) {
+bool adbd_auth_verify(const char* token, size_t token_size, const std::string& sig) {
static constexpr const char* key_paths[] = { "/adb_keys", "/data/misc/adb/adb_keys", nullptr };
for (const auto& path : key_paths) {
@@ -80,7 +80,8 @@
bool verified =
(RSA_verify(NID_sha1, reinterpret_cast<const uint8_t*>(token), token_size,
- reinterpret_cast<const uint8_t*>(sig), sig_len, key) == 1);
+ reinterpret_cast<const uint8_t*>(sig.c_str()), sig.size(),
+ key) == 1);
RSA_free(key);
if (verified) return true;
}
@@ -210,10 +211,10 @@
}
apacket* p = get_apacket();
- memcpy(p->data, t->token, sizeof(t->token));
p->msg.command = A_AUTH;
p->msg.arg0 = ADB_AUTH_TOKEN;
p->msg.data_length = sizeof(t->token);
+ p->payload.assign(t->token, t->token + sizeof(t->token));
send_packet(p, t);
}
diff --git a/adb/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
similarity index 100%
rename from adb/file_sync_service.cpp
rename to adb/daemon/file_sync_service.cpp
diff --git a/adb/framebuffer_service.cpp b/adb/daemon/framebuffer_service.cpp
similarity index 100%
rename from adb/framebuffer_service.cpp
rename to adb/daemon/framebuffer_service.cpp
diff --git a/adb/jdwp_service.cpp b/adb/daemon/jdwp_service.cpp
similarity index 98%
rename from adb/jdwp_service.cpp
rename to adb/daemon/jdwp_service.cpp
index 6f5396a..9761558 100644
--- a/adb/jdwp_service.cpp
+++ b/adb/daemon/jdwp_service.cpp
@@ -128,7 +128,7 @@
static void jdwp_process_list_updated(void);
struct JdwpProcess;
-static std::list<std::unique_ptr<JdwpProcess>> _jdwp_list;
+static auto& _jdwp_list = *new std::list<std::unique_ptr<JdwpProcess>>();
struct JdwpProcess {
explicit JdwpProcess(int socket) {
@@ -511,7 +511,7 @@
bool need_initial;
};
-static std::vector<std::unique_ptr<JdwpTracker>> _jdwp_trackers;
+static auto& _jdwp_trackers = *new std::vector<std::unique_ptr<JdwpTracker>>();
static void jdwp_process_list_updated(void) {
std::string data;
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 3c27582..4314dae 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -158,9 +158,10 @@
// descriptor will always be open.
adbd_cloexec_auth_socket();
- if (ALLOW_ADBD_NO_AUTH && !android::base::GetBoolProperty("ro.adb.secure", false)) {
- auth_required = false;
- }
+#if defined(ALLOW_ADBD_NO_AUTH)
+ // If ro.adb.secure is unset, default to no authentication required.
+ auth_required = android::base::GetBoolProperty("ro.adb.secure", false);
+#endif
adbd_auth_init();
diff --git a/adb/remount_service.cpp b/adb/daemon/remount_service.cpp
similarity index 79%
rename from adb/remount_service.cpp
rename to adb/daemon/remount_service.cpp
index 32ed28f..a4c7a5a 100644
--- a/adb/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -62,9 +62,9 @@
// The proc entry for / is full of lies, so check fstab instead.
// /proc/mounts lists rootfs and /dev/root, neither of which is what we want.
-static std::string find_mount(const char* dir) {
- if (strcmp(dir, "/") == 0) {
- return find_fstab_mount(dir);
+static std::string find_mount(const char* dir, bool is_root) {
+ if (is_root) {
+ return find_fstab_mount(dir);
} else {
return find_proc_mount(dir);
}
@@ -86,17 +86,29 @@
if (!directory_exists(dir)) {
return true;
}
- std::string dev = find_mount(dir);
- if (dev.empty()) {
+ bool is_root = strcmp(dir, "/") == 0;
+ std::string dev = find_mount(dir, is_root);
+ // Even if the device for the root is not found, we still try to remount it
+ // as rw. This typically only happens when running Android in a container:
+ // the root will almost always be in a loop device, which is dynamic, so
+ // it's not convenient to put in the fstab.
+ if (dev.empty() && !is_root) {
return true;
}
- if (!make_block_device_writable(dev)) {
+ if (!dev.empty() && !make_block_device_writable(dev)) {
WriteFdFmt(fd, "remount of %s failed; couldn't make block device %s writable: %s\n",
dir, dev.c_str(), strerror(errno));
return false;
}
+ if (mount(dev.c_str(), dir, "none", MS_REMOUNT | MS_BIND, nullptr) == -1) {
+ // This is useful for cases where the superblock is already marked as
+ // read-write, but the mount itself is read-only, such as containers
+ // where the remount with just MS_REMOUNT is forbidden by the kernel.
+ WriteFdFmt(fd, "remount of the %s mount failed: %s.\n", dir, strerror(errno));
+ return false;
+ }
if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) {
- WriteFdFmt(fd, "remount of %s failed: %s\n", dir, strerror(errno));
+ WriteFdFmt(fd, "remount of the %s superblock failed: %s\n", dir, strerror(errno));
return false;
}
return true;
diff --git a/adb/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp
similarity index 94%
rename from adb/set_verity_enable_state_service.cpp
rename to adb/daemon/set_verity_enable_state_service.cpp
index 49e0363..0fcf89b 100644
--- a/adb/set_verity_enable_state_service.cpp
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -98,13 +98,22 @@
return android::base::GetProperty("ro.boot.slot_suffix", "");
}
+static bool is_avb_device_locked() {
+ return android::base::GetProperty("ro.boot.vbmeta.device_state", "") == "locked";
+}
+
/* Use AVB to turn verity on/off */
static bool set_avb_verity_enabled_state(int fd, AvbOps* ops, bool enable_verity) {
std::string ab_suffix = get_ab_suffix();
-
bool verity_enabled;
+
+ if (is_avb_device_locked()) {
+ WriteFdFmt(fd, "Device is locked. Please unlock the device first\n");
+ return false;
+ }
+
if (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
- WriteFdFmt(fd, "Error getting verity state\n");
+ WriteFdFmt(fd, "Error getting verity state. Try adb root first?\n");
return false;
}
diff --git a/adb/shell_service.cpp b/adb/daemon/shell_service.cpp
similarity index 100%
rename from adb/shell_service.cpp
rename to adb/daemon/shell_service.cpp
diff --git a/adb/shell_service_test.cpp b/adb/daemon/shell_service_test.cpp
similarity index 100%
rename from adb/shell_service_test.cpp
rename to adb/daemon/shell_service_test.cpp
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 87ed3db..20fb6a3 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -317,6 +317,8 @@
goto err;
}
+ memset(&h->read_aiob.ctx, 0, sizeof(h->read_aiob.ctx));
+ memset(&h->write_aiob.ctx, 0, sizeof(h->write_aiob.ctx));
if (io_setup(USB_FFS_NUM_BUFS, &h->read_aiob.ctx) ||
io_setup(USB_FFS_NUM_BUFS, &h->write_aiob.ctx)) {
D("[ aio: got error on io_setup (%d) ]", errno);
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index b28de4b..d285561 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -26,6 +26,7 @@
#include <unistd.h>
#include <atomic>
+#include <deque>
#include <functional>
#include <list>
#include <mutex>
@@ -81,7 +82,7 @@
static auto& run_queue_notify_fd = *new unique_fd();
static auto& run_queue_mutex = *new std::mutex();
-static auto& run_queue GUARDED_BY(run_queue_mutex) = *new std::vector<std::function<void()>>();
+static auto& run_queue GUARDED_BY(run_queue_mutex) = *new std::deque<std::function<void()>>();
void check_main_thread() {
if (main_thread_valid) {
@@ -359,11 +360,21 @@
}
#endif // !ADB_HOST
-static void fdevent_run_flush() REQUIRES(run_queue_mutex) {
- for (auto& f : run_queue) {
- f();
+static void fdevent_run_flush() EXCLUDES(run_queue_mutex) {
+ // We need to be careful around reentrancy here, since a function we call can queue up another
+ // function.
+ while (true) {
+ std::function<void()> fn;
+ {
+ std::lock_guard<std::mutex> lock(run_queue_mutex);
+ if (run_queue.empty()) {
+ break;
+ }
+ fn = run_queue.front();
+ run_queue.pop_front();
+ }
+ fn();
}
- run_queue.clear();
}
static void fdevent_run_func(int fd, unsigned ev, void* /* userdata */) {
@@ -377,22 +388,23 @@
PLOG(FATAL) << "failed to empty run queue notify fd";
}
- std::lock_guard<std::mutex> lock(run_queue_mutex);
fdevent_run_flush();
}
static void fdevent_run_setup() {
- std::lock_guard<std::mutex> lock(run_queue_mutex);
- CHECK(run_queue_notify_fd.get() == -1);
- int s[2];
- if (adb_socketpair(s) != 0) {
- PLOG(FATAL) << "failed to create run queue notify socketpair";
- }
+ {
+ std::lock_guard<std::mutex> lock(run_queue_mutex);
+ CHECK(run_queue_notify_fd.get() == -1);
+ int s[2];
+ if (adb_socketpair(s) != 0) {
+ PLOG(FATAL) << "failed to create run queue notify socketpair";
+ }
- run_queue_notify_fd.reset(s[0]);
- fdevent* fde = fdevent_create(s[1], fdevent_run_func, nullptr);
- CHECK(fde != nullptr);
- fdevent_add(fde, FDE_READ);
+ run_queue_notify_fd.reset(s[0]);
+ fdevent* fde = fdevent_create(s[1], fdevent_run_func, nullptr);
+ CHECK(fde != nullptr);
+ fdevent_add(fde, FDE_READ);
+ }
fdevent_run_flush();
}
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index 86e0209..63cc4d1 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -194,3 +194,31 @@
ASSERT_EQ(i, vec[i]);
}
}
+
+static std::function<void()> make_appender(std::vector<int>* vec, int value) {
+ return [vec, value]() {
+ check_main_thread();
+ if (value == 100) {
+ return;
+ }
+
+ vec->push_back(value);
+ fdevent_run_on_main_thread(make_appender(vec, value + 1));
+ };
+}
+
+TEST_F(FdeventTest, run_on_main_thread_reentrant) {
+ std::vector<int> vec;
+
+ PrepareThread();
+ std::thread thread(fdevent_loop);
+
+ fdevent_run_on_main_thread(make_appender(&vec, 0));
+
+ TerminateThread(thread);
+
+ ASSERT_EQ(100u, vec.size());
+ for (int i = 0; i < 100; ++i) {
+ ASSERT_EQ(i, vec[i]);
+ }
+}
diff --git a/adb/protocol.txt b/adb/protocol.txt
index 55ea87f..f4523c4 100644
--- a/adb/protocol.txt
+++ b/adb/protocol.txt
@@ -183,9 +183,11 @@
Command constant: A_SYNC
-The SYNC message is used by the io pump to make sure that stale
+*** obsolete, no longer used ***
+
+The SYNC message was used by the io pump to make sure that stale
outbound messages are discarded when the connection to the remote side
-is broken. It is only used internally to the bridge and never valid
+is broken. It was only used internally to the bridge and never valid
to send across the wire.
* when the connection to the remote side goes offline, the io pump
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 307cbfe..0007fed 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -413,15 +413,15 @@
p->msg.command = A_WRTE;
p->msg.arg0 = s->peer->id;
p->msg.arg1 = s->id;
- p->msg.data_length = data.size();
- if (data.size() > sizeof(p->data)) {
+ if (data.size() > MAX_PAYLOAD) {
put_apacket(p);
return -1;
}
- // TODO: Convert apacket::data to a type that we can move into.
- memcpy(p->data, data.data(), data.size());
+ p->payload = std::move(data);
+ p->msg.data_length = p->payload.size();
+
send_packet(p, s->transport);
return 1;
}
@@ -482,17 +482,20 @@
void connect_to_remote(asocket* s, const char* destination) {
D("Connect_to_remote call RS(%d) fd=%d", s->id, s->fd);
apacket* p = get_apacket();
- size_t len = strlen(destination) + 1;
-
- if (len > (s->get_max_payload() - 1)) {
- fatal("destination oversized");
- }
D("LS(%d): connect('%s')", s->id, destination);
p->msg.command = A_OPEN;
p->msg.arg0 = s->id;
- p->msg.data_length = len;
- strcpy((char*)p->data, destination);
+
+ // adbd expects a null-terminated string.
+ p->payload = destination;
+ p->payload.push_back('\0');
+ p->msg.data_length = p->payload.size();
+
+ if (p->msg.data_length > s->get_max_payload()) {
+ fatal("destination oversized");
+ }
+
send_packet(p, s->transport);
}
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 9ae1297..3329f0f 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -28,23 +28,24 @@
#include <unistd.h>
#include <algorithm>
+#include <deque>
#include <list>
#include <mutex>
#include <thread>
#include <android-base/logging.h>
#include <android-base/parsenetaddress.h>
-#include <android-base/quick_exit.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/thread_annotations.h>
+#include <diagnose_usb.h>
+
#include "adb.h"
#include "adb_auth.h"
#include "adb_io.h"
#include "adb_trace.h"
#include "adb_utils.h"
-#include "diagnose_usb.h"
#include "fdevent.h"
static void transport_unref(atransport *t);
@@ -66,18 +67,96 @@
return next++;
}
+BlockingConnectionAdapter::BlockingConnectionAdapter(std::unique_ptr<BlockingConnection> connection)
+ : underlying_(std::move(connection)) {}
+
+BlockingConnectionAdapter::~BlockingConnectionAdapter() {
+ LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): destructing";
+ Stop();
+}
+
+void BlockingConnectionAdapter::Start() {
+ read_thread_ = std::thread([this]() {
+ LOG(INFO) << this->transport_name_ << ": read thread spawning";
+ while (true) {
+ std::unique_ptr<apacket> packet(new apacket());
+ if (!underlying_->Read(packet.get())) {
+ PLOG(INFO) << this->transport_name_ << ": read failed";
+ break;
+ }
+ read_callback_(this, std::move(packet));
+ }
+ std::call_once(this->error_flag_, [this]() { this->error_callback_(this, "read failed"); });
+ });
+
+ write_thread_ = std::thread([this]() {
+ LOG(INFO) << this->transport_name_ << ": write thread spawning";
+ while (true) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ cv_.wait(lock, [this]() { return this->stopped_ || !this->write_queue_.empty(); });
+
+ if (this->stopped_) {
+ return;
+ }
+
+ std::unique_ptr<apacket> packet = std::move(this->write_queue_.front());
+ this->write_queue_.pop_front();
+ lock.unlock();
+
+ if (!this->underlying_->Write(packet.get())) {
+ break;
+ }
+ }
+ std::call_once(this->error_flag_, [this]() { this->error_callback_(this, "write failed"); });
+ });
+}
+
+void BlockingConnectionAdapter::Stop() {
+ std::unique_lock<std::mutex> lock(mutex_);
+ if (stopped_) {
+ LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): already stopped";
+ return;
+ }
+
+ stopped_ = true;
+ lock.unlock();
+
+ LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): stopping";
+
+ this->underlying_->Close();
+
+ this->cv_.notify_one();
+ read_thread_.join();
+ write_thread_.join();
+
+ LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): stopped";
+ std::call_once(this->error_flag_, [this]() { this->error_callback_(this, "requested stop"); });
+}
+
+bool BlockingConnectionAdapter::Write(std::unique_ptr<apacket> packet) {
+ {
+ std::unique_lock<std::mutex> lock(this->mutex_);
+ write_queue_.emplace_back(std::move(packet));
+ }
+
+ cv_.notify_one();
+ return true;
+}
+
bool FdConnection::Read(apacket* packet) {
if (!ReadFdExactly(fd_.get(), &packet->msg, sizeof(amessage))) {
D("remote local: read terminated (message)");
return false;
}
- if (packet->msg.data_length > sizeof(packet->data)) {
+ if (packet->msg.data_length > MAX_PAYLOAD) {
D("remote local: read overflow (data length = %" PRIu32 ")", packet->msg.data_length);
return false;
}
- if (!ReadFdExactly(fd_.get(), &packet->data, packet->msg.data_length)) {
+ packet->payload.resize(packet->msg.data_length);
+
+ if (!ReadFdExactly(fd_.get(), &packet->payload[0], packet->payload.size())) {
D("remote local: terminated (data)");
return false;
}
@@ -86,13 +165,18 @@
}
bool FdConnection::Write(apacket* packet) {
- uint32_t length = packet->msg.data_length;
-
- if (!WriteFdExactly(fd_.get(), &packet->msg, sizeof(amessage) + length)) {
+ if (!WriteFdExactly(fd_.get(), &packet->msg, sizeof(packet->msg))) {
D("remote local: write terminated");
return false;
}
+ if (packet->msg.data_length) {
+ if (!WriteFdExactly(fd_.get(), &packet->payload[0], packet->msg.data_length)) {
+ D("remote local: write terminated");
+ return false;
+ }
+ }
+
return true;
}
@@ -133,70 +217,10 @@
std::string result = android::base::StringPrintf("%s: %s: [%s] arg0=%s arg1=%s (len=%d) ", name,
func, cmd, arg0, arg1, len);
- result += dump_hex(p->data, len);
+ result += dump_hex(p->payload.data(), p->payload.size());
return result;
}
-static int read_packet(int fd, const char* name, apacket** ppacket) {
- ATRACE_NAME("read_packet");
- char buff[8];
- if (!name) {
- snprintf(buff, sizeof buff, "fd=%d", fd);
- name = buff;
- }
- char* p = reinterpret_cast<char*>(ppacket); /* really read a packet address */
- int len = sizeof(apacket*);
- while (len > 0) {
- int r = adb_read(fd, p, len);
- if (r > 0) {
- len -= r;
- p += r;
- } else {
- D("%s: read_packet (fd=%d), error ret=%d: %s", name, fd, r, strerror(errno));
- return -1;
- }
- }
-
- VLOG(TRANSPORT) << dump_packet(name, "from remote", *ppacket);
- return 0;
-}
-
-static int write_packet(int fd, const char* name, apacket** ppacket) {
- ATRACE_NAME("write_packet");
- char buff[8];
- if (!name) {
- snprintf(buff, sizeof buff, "fd=%d", fd);
- name = buff;
- }
- VLOG(TRANSPORT) << dump_packet(name, "to remote", *ppacket);
- char* p = reinterpret_cast<char*>(ppacket); /* we really write the packet address */
- int len = sizeof(apacket*);
- while (len > 0) {
- int r = adb_write(fd, p, len);
- if (r > 0) {
- len -= r;
- p += r;
- } else {
- D("%s: write_packet (fd=%d) error ret=%d: %s", name, fd, r, strerror(errno));
- return -1;
- }
- }
- return 0;
-}
-
-static void transport_socket_events(int fd, unsigned events, void* _t) {
- atransport* t = reinterpret_cast<atransport*>(_t);
- D("transport_socket_events(fd=%d, events=%04x,...)", fd, events);
- if (events & FDE_READ) {
- apacket* p = 0;
- if (read_packet(fd, t->serial, &p)) {
- D("%s: failed to read packet from transport socket on fd %d", t->serial, fd);
- } else {
- handle_packet(p, (atransport*)_t);
- }
- }
-}
-
void send_packet(apacket* p, atransport* t) {
p->msg.magic = p->msg.command ^ 0xffffffff;
// compute a checksum for connection/auth packets for compatibility reasons
@@ -206,154 +230,18 @@
p->msg.data_check = calculate_apacket_checksum(p);
}
- print_packet("send", p);
+ VLOG(TRANSPORT) << dump_packet(t->serial, "to remote", p);
if (t == NULL) {
fatal("Transport is null");
}
- if (write_packet(t->transport_socket, t->serial, &p)) {
- fatal_errno("cannot enqueue packet on transport socket");
+ if (t->Write(p) != 0) {
+ D("%s: failed to enqueue packet, closing transport", t->serial);
+ t->Kick();
}
}
-// The transport is opened by transport_register_func before
-// the read_transport and write_transport threads are started.
-//
-// The read_transport thread issues a SYNC(1, token) message to let
-// the write_transport thread know to start things up. In the event
-// of transport IO failure, the read_transport thread will post a
-// SYNC(0,0) message to ensure shutdown.
-//
-// The transport will not actually be closed until both threads exit, but the threads
-// will kick the transport on their way out to disconnect the underlying device.
-//
-// read_transport thread reads data from a transport (representing a usb/tcp connection),
-// and makes the main thread call handle_packet().
-static void read_transport_thread(void* _t) {
- atransport* t = reinterpret_cast<atransport*>(_t);
- apacket* p;
-
- adb_thread_setname(
- android::base::StringPrintf("<-%s", (t->serial != nullptr ? t->serial : "transport")));
- D("%s: starting read_transport thread on fd %d, SYNC online (%d)", t->serial, t->fd,
- t->sync_token + 1);
- p = get_apacket();
- p->msg.command = A_SYNC;
- p->msg.arg0 = 1;
- p->msg.arg1 = ++(t->sync_token);
- p->msg.magic = A_SYNC ^ 0xffffffff;
- if (write_packet(t->fd, t->serial, &p)) {
- put_apacket(p);
- D("%s: failed to write SYNC packet", t->serial);
- goto oops;
- }
-
- D("%s: data pump started", t->serial);
- for (;;) {
- ATRACE_NAME("read_transport loop");
- p = get_apacket();
-
- {
- ATRACE_NAME("read_transport read_remote");
- if (!t->connection->Read(p)) {
- D("%s: remote read failed for transport", t->serial);
- put_apacket(p);
- break;
- }
-
- if (!check_header(p, t)) {
- D("%s: remote read: bad header", t->serial);
- put_apacket(p);
- break;
- }
-
-#if ADB_HOST
- if (p->msg.command == 0) {
- put_apacket(p);
- continue;
- }
-#endif
- }
-
- D("%s: received remote packet, sending to transport", t->serial);
- if (write_packet(t->fd, t->serial, &p)) {
- put_apacket(p);
- D("%s: failed to write apacket to transport", t->serial);
- goto oops;
- }
- }
-
- D("%s: SYNC offline for transport", t->serial);
- p = get_apacket();
- p->msg.command = A_SYNC;
- p->msg.arg0 = 0;
- p->msg.arg1 = 0;
- p->msg.magic = A_SYNC ^ 0xffffffff;
- if (write_packet(t->fd, t->serial, &p)) {
- put_apacket(p);
- D("%s: failed to write SYNC apacket to transport", t->serial);
- }
-
-oops:
- D("%s: read_transport thread is exiting", t->serial);
- kick_transport(t);
- transport_unref(t);
-}
-
-// write_transport thread gets packets sent by the main thread (through send_packet()),
-// and writes to a transport (representing a usb/tcp connection).
-static void write_transport_thread(void* _t) {
- atransport* t = reinterpret_cast<atransport*>(_t);
- apacket* p;
- int active = 0;
-
- adb_thread_setname(
- android::base::StringPrintf("->%s", (t->serial != nullptr ? t->serial : "transport")));
- D("%s: starting write_transport thread, reading from fd %d", t->serial, t->fd);
-
- for (;;) {
- ATRACE_NAME("write_transport loop");
- if (read_packet(t->fd, t->serial, &p)) {
- D("%s: failed to read apacket from transport on fd %d", t->serial, t->fd);
- break;
- }
-
- if (p->msg.command == A_SYNC) {
- if (p->msg.arg0 == 0) {
- D("%s: transport SYNC offline", t->serial);
- put_apacket(p);
- break;
- } else {
- if (p->msg.arg1 == t->sync_token) {
- D("%s: transport SYNC online", t->serial);
- active = 1;
- } else {
- D("%s: transport ignoring SYNC %d != %d", t->serial, p->msg.arg1, t->sync_token);
- }
- }
- } else {
- if (active) {
- D("%s: transport got packet, sending to remote", t->serial);
- ATRACE_NAME("write_transport write_remote");
- if (t->Write(p) != 0) {
- D("%s: remote write failed for transport", t->serial);
- put_apacket(p);
- break;
- }
- } else {
- D("%s: transport ignoring packet while offline", t->serial);
- }
- }
-
- put_apacket(p);
- }
-
- D("%s: write_transport thread is exiting, fd %d", t->serial, t->fd);
- kick_transport(t);
- transport_unref(t);
-}
-
void kick_transport(atransport* t) {
std::lock_guard<std::recursive_mutex> lock(transport_lock);
// As kick_transport() can be called from threads without guarantee that t is valid,
@@ -544,9 +432,10 @@
return 0;
}
-static void transport_registration_func(int _fd, unsigned ev, void* data) {
+static void remove_transport(atransport*);
+
+static void transport_registration_func(int _fd, unsigned ev, void*) {
tmsg m;
- int s[2];
atransport* t;
if (!(ev & FDE_READ)) {
@@ -560,13 +449,7 @@
t = m.transport;
if (m.action == 0) {
- D("transport: %s removing and free'ing %d", t->serial, t->transport_socket);
-
- /* IMPORTANT: the remove closes one half of the
- ** socket pair. The close closes the other half.
- */
- fdevent_remove(&(t->transport_fde));
- adb_close(t->fd);
+ D("transport: %s deleting", t->serial);
{
std::lock_guard<std::recursive_mutex> lock(transport_lock);
@@ -588,23 +471,33 @@
/* don't create transport threads for inaccessible devices */
if (t->GetConnectionState() != kCsNoPerm) {
/* initial references are the two threads */
- t->ref_count = 2;
+ t->ref_count = 1;
+ t->connection->SetTransportName(t->serial_name());
+ t->connection->SetReadCallback([t](Connection*, std::unique_ptr<apacket> p) {
+ if (!check_header(p.get(), t)) {
+ D("%s: remote read: bad header", t->serial);
+ return false;
+ }
- if (adb_socketpair(s)) {
- fatal_errno("cannot open transport socketpair");
- }
+ VLOG(TRANSPORT) << dump_packet(t->serial, "from remote", p.get());
+ apacket* packet = p.release();
- D("transport: %s socketpair: (%d,%d) starting", t->serial, s[0], s[1]);
+ // TODO: Does this need to run on the main thread?
+ fdevent_run_on_main_thread([packet, t]() { handle_packet(packet, t); });
+ return true;
+ });
+ t->connection->SetErrorCallback([t](Connection*, const std::string& error) {
+ D("%s: connection terminated: %s", t->serial, error.c_str());
+ fdevent_run_on_main_thread([t]() {
+ handle_offline(t);
+ transport_unref(t);
+ });
+ });
- t->transport_socket = s[0];
- t->fd = s[1];
-
- fdevent_install(&(t->transport_fde), t->transport_socket, transport_socket_events, t);
-
- fdevent_set(&(t->transport_fde), FDE_READ);
-
- std::thread(write_transport_thread, t).detach();
- std::thread(read_transport_thread, t).detach();
+ t->connection->Start();
+#if ADB_HOST
+ send_connect(t);
+#endif
}
{
@@ -670,7 +563,7 @@
t->ref_count--;
if (t->ref_count == 0) {
D("transport: %s unref (kicking and closing)", t->serial);
- t->connection->Close();
+ t->connection->Stop();
remove_transport(t);
} else {
D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
@@ -721,9 +614,7 @@
std::unique_lock<std::recursive_mutex> lock(transport_lock);
for (const auto& t : transport_list) {
if (t->GetConnectionState() == kCsNoPerm) {
-#if ADB_HOST
*error_out = UsbNoPermissionsLongHelpText();
-#endif
continue;
}
@@ -798,14 +689,14 @@
}
int atransport::Write(apacket* p) {
- return this->connection->Write(p) ? 0 : -1;
+ return this->connection->Write(std::unique_ptr<apacket>(p)) ? 0 : -1;
}
void atransport::Kick() {
if (!kicked_) {
D("kicking transport %s", this->serial);
kicked_ = true;
- this->connection->Close();
+ this->connection->Stop();
}
}
@@ -818,7 +709,7 @@
connection_state_ = state;
}
-const std::string atransport::connection_state_name() const {
+std::string atransport::connection_state_name() const {
ConnectionState state = GetConnectionState();
switch (state) {
case kCsOffline:
diff --git a/adb/transport.h b/adb/transport.h
index 9700f44..8b71e34 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -20,12 +20,14 @@
#include <sys/types.h>
#include <atomic>
+#include <condition_variable>
#include <deque>
#include <functional>
#include <list>
#include <memory>
#include <mutex>
#include <string>
+#include <thread>
#include <unordered_set>
#include <openssl/rsa.h>
@@ -57,15 +59,47 @@
TransportId NextTransportId();
-// Abstraction for a blocking packet transport.
+// Abstraction for a non-blocking packet transport.
struct Connection {
Connection() = default;
- Connection(const Connection& copy) = delete;
- Connection(Connection&& move) = delete;
-
- // Destroy a Connection. Formerly known as 'Close' in atransport.
virtual ~Connection() = default;
+ void SetTransportName(std::string transport_name) {
+ transport_name_ = std::move(transport_name);
+ }
+
+ using ReadCallback = std::function<bool(Connection*, std::unique_ptr<apacket>)>;
+ void SetReadCallback(ReadCallback callback) {
+ CHECK(!read_callback_);
+ read_callback_ = callback;
+ }
+
+ // Called after the Connection has terminated, either by an error or because Stop was called.
+ using ErrorCallback = std::function<void(Connection*, const std::string&)>;
+ void SetErrorCallback(ErrorCallback callback) {
+ CHECK(!error_callback_);
+ error_callback_ = callback;
+ }
+
+ virtual bool Write(std::unique_ptr<apacket> packet) = 0;
+
+ virtual void Start() = 0;
+ virtual void Stop() = 0;
+
+ std::string transport_name_;
+ ReadCallback read_callback_;
+ ErrorCallback error_callback_;
+};
+
+// Abstraction for a blocking packet transport.
+struct BlockingConnection {
+ BlockingConnection() = default;
+ BlockingConnection(const BlockingConnection& copy) = delete;
+ BlockingConnection(BlockingConnection&& move) = delete;
+
+ // Destroy a BlockingConnection. Formerly known as 'Close' in atransport.
+ virtual ~BlockingConnection() = default;
+
// Read/Write a packet. These functions are concurrently called from a transport's reader/writer
// threads.
virtual bool Read(apacket* packet) = 0;
@@ -77,7 +111,30 @@
virtual void Close() = 0;
};
-struct FdConnection : public Connection {
+struct BlockingConnectionAdapter : public Connection {
+ explicit BlockingConnectionAdapter(std::unique_ptr<BlockingConnection> connection);
+
+ virtual ~BlockingConnectionAdapter();
+
+ virtual bool Write(std::unique_ptr<apacket> packet) override final;
+
+ virtual void Start() override final;
+ virtual void Stop() override final;
+
+ bool stopped_ = false;
+
+ std::unique_ptr<BlockingConnection> underlying_;
+ std::thread read_thread_;
+ std::thread write_thread_;
+
+ std::deque<std::unique_ptr<apacket>> write_queue_;
+ std::mutex mutex_;
+ std::condition_variable cv_;
+
+ std::once_flag error_flag_;
+};
+
+struct FdConnection : public BlockingConnection {
explicit FdConnection(unique_fd fd) : fd_(std::move(fd)) {}
bool Read(apacket* packet) override final;
@@ -89,7 +146,7 @@
unique_fd fd_;
};
-struct UsbConnection : public Connection {
+struct UsbConnection : public BlockingConnection {
explicit UsbConnection(usb_handle* handle) : handle_(handle) {}
~UsbConnection();
@@ -110,7 +167,6 @@
atransport(ConnectionState state = kCsOffline)
: id(NextTransportId()), connection_state_(state) {
- transport_fde = {};
// Initialize protocol to min version for compatibility with older versions.
// Version will be updated post-connect.
protocol_version = A_VERSION_MIN;
@@ -126,11 +182,7 @@
void SetConnectionState(ConnectionState state);
const TransportId id;
- int fd = -1;
- int transport_socket = -1;
- fdevent transport_fde;
size_t ref_count = 0;
- uint32_t sync_token = 0;
bool online = false;
TransportType type = kTransportAny;
@@ -152,8 +204,8 @@
char token[TOKEN_SIZE] = {};
size_t failed_auth_attempts = 0;
- const std::string serial_name() const { return serial ? serial : "<unknown>"; }
- const std::string connection_state_name() const;
+ std::string serial_name() const { return serial ? serial : "<unknown>"; }
+ std::string connection_state_name() const;
void update_version(int version, size_t payload);
int get_protocol_version() const;
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 560a031..ff395dc 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -445,13 +445,14 @@
int fail = 0;
unique_fd fd(s);
- t->sync_token = 1;
t->type = kTransportLocal;
#if ADB_HOST
// Emulator connection.
if (local) {
- t->connection.reset(new EmulatorConnection(std::move(fd), adb_port));
+ std::unique_ptr<BlockingConnection> emulator_connection(
+ new EmulatorConnection(std::move(fd), adb_port));
+ t->connection.reset(new BlockingConnectionAdapter(std::move(emulator_connection)));
std::lock_guard<std::mutex> lock(local_transports_lock);
atransport* existing_transport = find_emulator_transport_by_adb_port_locked(adb_port);
if (existing_transport != NULL) {
@@ -470,6 +471,7 @@
#endif
// Regular tcp connection.
- t->connection.reset(new FdConnection(std::move(fd)));
+ std::unique_ptr<BlockingConnection> fd_connection(new FdConnection(std::move(fd)));
+ t->connection.reset(new BlockingConnectionAdapter(std::move(fd_connection)));
return fail;
}
diff --git a/adb/transport_mdns_unsupported.cpp b/adb/transport_mdns_unsupported.cpp
deleted file mode 100644
index 387d341..0000000
--- a/adb/transport_mdns_unsupported.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * 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.
- */
-
-/* For when mDNS discovery is unsupported */
-void init_mdns_transport_discovery(void) {}
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index a108699..33e00a1 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -61,13 +61,12 @@
static int UsbReadPayload(usb_handle* h, apacket* p) {
D("UsbReadPayload(%d)", p->msg.data_length);
- if (p->msg.data_length > sizeof(p->data)) {
+ if (p->msg.data_length > MAX_PAYLOAD) {
return -1;
}
#if CHECK_PACKET_OVERFLOW
size_t usb_packet_size = usb_get_max_packet_size(h);
- CHECK_EQ(0ULL, sizeof(p->data) % usb_packet_size);
// Round the data length up to the nearest packet size boundary.
// The device won't send a zero packet for packet size aligned payloads,
@@ -77,10 +76,18 @@
if (rem_size) {
len += usb_packet_size - rem_size;
}
- CHECK_LE(len, sizeof(p->data));
- return usb_read(h, &p->data, len);
+
+ p->payload.resize(len);
+ int rc = usb_read(h, &p->payload[0], p->payload.size());
+ if (rc != static_cast<int>(p->msg.data_length)) {
+ return -1;
+ }
+
+ p->payload.resize(rc);
+ return rc;
#else
- return usb_read(h, &p->data, p->msg.data_length);
+ p->payload.resize(p->msg.data_length);
+ return usb_read(h, &p->payload[0], p->payload.size());
#endif
}
@@ -120,12 +127,13 @@
}
if (p->msg.data_length) {
- if (p->msg.data_length > sizeof(p->data)) {
+ if (p->msg.data_length > MAX_PAYLOAD) {
PLOG(ERROR) << "remote usb: read overflow (data length = " << p->msg.data_length << ")";
return -1;
}
- if (usb_read(usb, p->data, p->msg.data_length)) {
+ p->payload.resize(p->msg.data_length);
+ if (usb_read(usb, &p->payload[0], p->payload.size())) {
PLOG(ERROR) << "remote usb: terminated (data)";
return -1;
}
@@ -152,7 +160,7 @@
return false;
}
- if (packet->msg.data_length != 0 && usb_write(handle_, &packet->data, size) != 0) {
+ if (packet->msg.data_length != 0 && usb_write(handle_, packet->payload.data(), size) != 0) {
PLOG(ERROR) << "remote usb: 2 - write terminated";
return false;
}
@@ -166,8 +174,8 @@
void init_usb_transport(atransport* t, usb_handle* h) {
D("transport: usb");
- t->connection.reset(new UsbConnection(h));
- t->sync_token = 1;
+ std::unique_ptr<BlockingConnection> connection(new UsbConnection(h));
+ t->connection.reset(new BlockingConnectionAdapter(std::move(connection)));
t->type = kTransportUsb;
}
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index afff2c9..cc7aaf6 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -105,6 +105,9 @@
void DefaultAborter(const char* abort_message);
+std::string GetDefaultTag();
+void SetDefaultTag(const std::string& tag);
+
#ifdef __ANDROID__
// We expose this even though it is the default because a user that wants to
// override the default log buffer will have to construct this themselves.
diff --git a/base/include/android-base/strings.h b/base/include/android-base/strings.h
index c11acb1..4d9fa34 100644
--- a/base/include/android-base/strings.h
+++ b/base/include/android-base/strings.h
@@ -67,8 +67,8 @@
// TODO: string_view
bool EndsWith(const std::string& s, const char* suffix);
bool EndsWithIgnoreCase(const std::string& s, const char* suffix);
-bool EndsWith(const std::string& s, const std::string& prefix);
-bool EndsWithIgnoreCase(const std::string& s, const std::string& prefix);
+bool EndsWith(const std::string& s, const std::string& suffix);
+bool EndsWithIgnoreCase(const std::string& s, const std::string& suffix);
// Tests whether 'lhs' equals 'rhs', ignoring case.
bool EqualsIgnoreCase(const std::string& lhs, const std::string& rhs);
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index 2edafe3..b95fa07 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -31,6 +31,8 @@
// Release the ownership of fd, caller is reponsible for closing the
// fd or stream properly.
int release();
+ // Don't remove the temporary file in the destructor.
+ void DoNotRemove() { remove_file_ = false; }
int fd;
char path[1024];
@@ -38,6 +40,8 @@
private:
void init(const std::string& tmp_dir);
+ bool remove_file_ = true;
+
DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
};
diff --git a/base/logging.cpp b/base/logging.cpp
index 1f7bc2a..a31feef 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -139,9 +139,27 @@
return aborter;
}
-static std::string& ProgramInvocationName() {
- static auto& programInvocationName = *new std::string(getprogname());
- return programInvocationName;
+static std::recursive_mutex& TagLock() {
+ static auto& tag_lock = *new std::recursive_mutex();
+ return tag_lock;
+}
+static std::string* gDefaultTag;
+std::string GetDefaultTag() {
+ std::lock_guard<std::recursive_mutex> lock(TagLock());
+ if (gDefaultTag == nullptr) {
+ return "";
+ }
+ return *gDefaultTag;
+}
+void SetDefaultTag(const std::string& tag) {
+ std::lock_guard<std::recursive_mutex> lock(TagLock());
+ if (gDefaultTag != nullptr) {
+ delete gDefaultTag;
+ gDefaultTag = nullptr;
+ }
+ if (!tag.empty()) {
+ gDefaultTag = new std::string(tag);
+ }
}
static bool gInitialized = false;
@@ -269,8 +287,7 @@
// Linux to recover this, but we don't have that luxury on the Mac/Windows,
// and there are a couple of argv[0] variants that are commonly used.
if (argv != nullptr) {
- std::lock_guard<std::mutex> lock(LoggingLock());
- ProgramInvocationName() = basename(argv[0]);
+ SetDefaultTag(basename(argv[0]));
}
const char* tags = getenv("ANDROID_LOG_TAGS");
@@ -448,8 +465,15 @@
void LogMessage::LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity,
const char* tag, const char* message) {
- if (tag == nullptr) tag = ProgramInvocationName().c_str();
- Logger()(id, severity, tag, file, line, message);
+ if (tag == nullptr) {
+ std::lock_guard<std::recursive_mutex> lock(TagLock());
+ if (gDefaultTag == nullptr) {
+ gDefaultTag = new std::string(getprogname());
+ }
+ Logger()(id, severity, gDefaultTag->c_str(), file, line, message);
+ } else {
+ Logger()(id, severity, tag, file, line, message);
+ }
}
void LogMessage::LogLine(const char* file, unsigned int line, LogId id, LogSeverity severity,
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index 6f05d9b..5f689fa 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -206,8 +206,8 @@
}
#endif
-static void CheckMessage(const CapturedStderr& cap,
- android::base::LogSeverity severity, const char* expected) {
+static void CheckMessage(const CapturedStderr& cap, android::base::LogSeverity severity,
+ const char* expected, const char* expected_tag = nullptr) {
std::string output;
ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
android::base::ReadFdToString(cap.fd(), &output);
@@ -217,9 +217,18 @@
// many characters are in the log message.
ASSERT_GT(output.length(), strlen(expected));
ASSERT_NE(nullptr, strstr(output.c_str(), expected)) << output;
+ if (expected_tag != nullptr) {
+ ASSERT_NE(nullptr, strstr(output.c_str(), expected_tag)) << output;
+ }
#if !defined(_WIN32)
- std::regex message_regex(make_log_pattern(severity, expected));
+ std::string regex_str;
+ if (expected_tag != nullptr) {
+ regex_str.append(expected_tag);
+ regex_str.append(" ");
+ }
+ regex_str.append(make_log_pattern(severity, expected));
+ std::regex message_regex(regex_str);
ASSERT_TRUE(std::regex_search(output, message_regex)) << output;
#endif
}
@@ -600,3 +609,17 @@
__attribute__((constructor)) void TestLoggingInConstructor() {
LOG(ERROR) << "foobar";
}
+
+TEST(logging, SetDefaultTag) {
+ constexpr const char* expected_tag = "test_tag";
+ constexpr const char* expected_msg = "foobar";
+ CapturedStderr cap;
+ {
+ std::string old_default_tag = android::base::GetDefaultTag();
+ android::base::SetDefaultTag(expected_tag);
+ android::base::ScopedLogSeverity sls(android::base::LogSeverity::INFO);
+ LOG(INFO) << expected_msg;
+ android::base::SetDefaultTag(old_default_tag);
+ }
+ CheckMessage(cap, android::base::LogSeverity::INFO, expected_msg, expected_tag);
+}
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 9d8dfb2..1619c21 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -92,7 +92,9 @@
if (fd != -1) {
close(fd);
}
- unlink(path);
+ if (remove_file_) {
+ unlink(path);
+ }
}
int TemporaryFile::release() {
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 79702a6..f5d789c 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -287,6 +287,7 @@
bootstat: Service started: /system/bin/bootstat --record_boot_reason
bootstat: Service started: /system/bin/bootstat --record_time_since_factory_reset
bootstat: Service started: /system/bin/bootstat -l
+bootstat: Service started: /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
bootstat: Battery level at shutdown 100%
bootstat: Battery level at startup 100%
init : Parsing file /system/etc/init/bootstat.rc...
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index 3bc1742..c03b41d 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -20,4 +20,5 @@
LOCAL_SRC_FILES_arm64 := seccomp_policy/crash_dump.arm64.policy
LOCAL_SRC_FILES_x86 := seccomp_policy/crash_dump.x86.policy
LOCAL_SRC_FILES_x86_64 := seccomp_policy/crash_dump.x86_64.policy
+LOCAL_MODULE_TARGET_ARCH := arm arm64 x86 x86_64
include $(BUILD_PREBUILT)
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index f8b4bad..397ff2f 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -20,6 +20,7 @@
#include <sys/capability.h>
#include <sys/prctl.h>
#include <sys/ptrace.h>
+#include <sys/resource.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
@@ -570,7 +571,7 @@
static const char* const kDebuggerdSeccompPolicy =
"/system/etc/seccomp_policy/crash_dump." ABI_STRING ".policy";
-pid_t seccomp_fork() {
+static pid_t seccomp_fork_impl(void (*prejail)()) {
unique_fd policy_fd(open(kDebuggerdSeccompPolicy, O_RDONLY | O_CLOEXEC));
if (policy_fd == -1) {
LOG(FATAL) << "failed to open policy " << kDebuggerdSeccompPolicy;
@@ -607,10 +608,18 @@
continue;
}
+ if (prejail) {
+ prejail();
+ }
+
minijail_enter(jail.get());
return result;
}
+static pid_t seccomp_fork() {
+ return seccomp_fork_impl(nullptr);
+}
+
TEST_F(CrasherTest, seccomp_crash) {
int intercept_result;
unique_fd output_fd;
@@ -628,6 +637,46 @@
ASSERT_BACKTRACE_FRAME(result, "abort");
}
+static pid_t seccomp_fork_rlimit() {
+ return seccomp_fork_impl([]() {
+ struct rlimit rlim = {
+ .rlim_cur = 512 * 1024 * 1024,
+ .rlim_max = 512 * 1024 * 1024,
+ };
+
+ if (setrlimit(RLIMIT_AS, &rlim) != 0) {
+ raise(SIGINT);
+ }
+ });
+}
+
+TEST_F(CrasherTest, seccomp_crash_oom) {
+ int intercept_result;
+ unique_fd output_fd;
+
+ StartProcess(
+ []() {
+ std::vector<void*> vec;
+ for (int i = 0; i < 512; ++i) {
+ char* buf = static_cast<char*>(malloc(1024 * 1024));
+ if (!buf) {
+ abort();
+ }
+ memset(buf, 0xff, 1024 * 1024);
+ vec.push_back(buf);
+ }
+ },
+ &seccomp_fork_rlimit);
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(SIGABRT);
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ // We can't actually generate a backtrace, just make sure that the process terminates.
+}
+
__attribute__((noinline)) extern "C" bool raise_debugger_signal(DebuggerdDumpType dump_type) {
siginfo_t siginfo;
siginfo.si_code = SI_QUEUE;
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 364fca5..dea2e17 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -37,6 +37,7 @@
#include <atomic>
#include <memory>
+#include <mutex>
#include <android-base/file.h>
#include <android-base/unique_fd.h>
@@ -298,11 +299,13 @@
static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) {
// Only allow one thread to handle a crash at a time (this can happen multiple times without
// exit, since tombstones can be requested without a real crash happening.)
- static pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;
- int ret = pthread_mutex_lock(&crash_mutex);
- if (ret != 0) {
- async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_lock failed: %s", strerror(ret));
- return;
+ static std::recursive_mutex crash_mutex;
+ static int lock_count;
+
+ crash_mutex.lock();
+ if (lock_count++ > 0) {
+ async_safe_format_log(ANDROID_LOG_ERROR, "libc", "recursed signal handler call, exiting");
+ _exit(1);
}
unique_fd tombstone_socket, output_fd;
@@ -313,7 +316,8 @@
tombstoned_notify_completion(tombstone_socket.get());
}
- pthread_mutex_unlock(&crash_mutex);
+ --lock_count;
+ crash_mutex.unlock();
}
extern "C" void debuggerd_fallback_handler(siginfo_t* info, ucontext_t* ucontext,
diff --git a/diagnose_usb/Android.bp b/diagnose_usb/Android.bp
new file mode 100644
index 0000000..a7ecf37
--- /dev/null
+++ b/diagnose_usb/Android.bp
@@ -0,0 +1,13 @@
+cc_library_static {
+ name: "libdiagnose_usb",
+ cflags: ["-Wall", "-Wextra", "-Werror"],
+ host_supported: true,
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+ srcs: ["diagnose_usb.cpp"],
+ export_include_dirs: ["include"],
+ static_libs: ["libbase"],
+}
diff --git a/diagnose_usb/OWNERS b/diagnose_usb/OWNERS
new file mode 100644
index 0000000..643b448
--- /dev/null
+++ b/diagnose_usb/OWNERS
@@ -0,0 +1,2 @@
+jmgao@google.com
+yabinc@google.com
diff --git a/adb/diagnose_usb.cpp b/diagnose_usb/diagnose_usb.cpp
similarity index 97%
rename from adb/diagnose_usb.cpp
rename to diagnose_usb/diagnose_usb.cpp
index 9f721bf..5695ece 100644
--- a/adb/diagnose_usb.cpp
+++ b/diagnose_usb/diagnose_usb.cpp
@@ -33,7 +33,7 @@
// Returns a message describing any potential problems we find with udev, or an empty string if we
// can't find plugdev information (i.e. udev is not installed).
static std::string GetUdevProblem() {
-#if defined(__linux__)
+#if defined(__linux__) && !defined(__BIONIC__)
errno = 0;
group* plugdev_group = getgrnam("plugdev");
diff --git a/adb/diagnose_usb.h b/diagnose_usb/include/diagnose_usb.h
similarity index 100%
rename from adb/diagnose_usb.h
rename to diagnose_usb/include/diagnose_usb.h
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index dfcf090..f5bcc26 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -39,13 +39,15 @@
LOCAL_MODULE_TAGS := debug
LOCAL_MODULE_HOST_OS := darwin linux windows
LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
-LOCAL_REQUIRED_MODULES := mke2fs e2fsdroid mke2fs.conf make_f2fs sload_f2fs
+LOCAL_REQUIRED_MODULES := mke2fs make_f2fs
LOCAL_SRC_FILES_linux := usb_linux.cpp
LOCAL_STATIC_LIBRARIES_linux := libselinux
+LOCAL_REQUIRED_MODULES_linux := e2fsdroid mke2fs.conf sload_f2fs
LOCAL_SRC_FILES_darwin := usb_osx.cpp
LOCAL_STATIC_LIBRARIES_darwin := libselinux
+LOCAL_REQUIRED_MODULES_darwin := e2fsdroid mke2fs.conf sload_f2fs
LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
LOCAL_CFLAGS_darwin := -Wno-unused-parameter
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 1c01d8c..72a65d2 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -33,12 +33,15 @@
#include "fs_mgr_priv.h"
+using android::base::StartsWith;
+
const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android");
struct fs_mgr_flag_values {
char *key_loc;
char* key_dir;
char *verity_loc;
+ char *sysfs_path;
long long part_length;
char *label;
int partnum;
@@ -104,6 +107,7 @@
{"quota", MF_QUOTA},
{"eraseblk=", MF_ERASEBLKSIZE},
{"logicalblk=", MF_LOGICALBLKSIZE},
+ {"sysfs_path=", MF_SYSFS},
{"defaults", 0},
{0, 0},
};
@@ -341,6 +345,9 @@
unsigned int val = strtoul(strchr(p, '=') + 1, NULL, 0);
if (val >= 4096 && (val & (val - 1)) == 0)
flag_vals->logical_blk_size = val;
+ } else if ((fl[i].flag == MF_SYSFS) && flag_vals) {
+ /* The path to trigger device gc by idle-maint of vold. */
+ flag_vals->sysfs_path = strdup(strchr(p, '=') + 1);
}
break;
}
@@ -434,6 +441,10 @@
LERROR << "dt_fstab: Failed to find device for partition " << dp->d_name;
return {};
}
+ if (!StartsWith(value, "/dev")) {
+ LERROR << "dt_fstab: Invalid device node for partition " << dp->d_name;
+ return {};
+ }
fstab_entry.push_back(value);
std::string mount_point;
@@ -615,6 +626,7 @@
fstab->recs[cnt].file_names_mode = flag_vals.file_names_mode;
fstab->recs[cnt].erase_blk_size = flag_vals.erase_blk_size;
fstab->recs[cnt].logical_blk_size = flag_vals.logical_blk_size;
+ fstab->recs[cnt].sysfs_path = flag_vals.sysfs_path;
cnt++;
}
/* If an A/B partition, modify block device to be the real block device */
@@ -787,6 +799,7 @@
free(fstab->recs[i].key_loc);
free(fstab->recs[i].key_dir);
free(fstab->recs[i].label);
+ free(fstab->recs[i].sysfs_path);
}
/* Free the fstab_recs array created by calloc(3) */
@@ -922,3 +935,8 @@
int fs_mgr_is_quota(const struct fstab_rec* fstab) {
return fstab->fs_mgr_flags & MF_QUOTA;
}
+
+int fs_mgr_has_sysfs_path(const struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_SYSFS;
+}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 724156d..ade0cc4 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -109,7 +109,8 @@
#define MF_ERASEBLKSIZE 0x800000
#define MF_LOGICALBLKSIZE 0X1000000
#define MF_AVB 0X2000000
-#define MF_KEYDIRECTORY 0X4000000
+#define MF_KEYDIRECTORY 0X4000000
+#define MF_SYSFS 0X8000000
#define DM_BUF_SIZE 4096
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 94aacfd..8c585dd 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -56,6 +56,7 @@
unsigned int file_names_mode;
unsigned int erase_blk_size;
unsigned int logical_blk_size;
+ char* sysfs_path;
};
struct fstab* fs_mgr_read_fstab_default();
@@ -83,6 +84,7 @@
int fs_mgr_is_nofail(const struct fstab_rec* fstab);
int fs_mgr_is_latemount(const struct fstab_rec* fstab);
int fs_mgr_is_quota(const struct fstab_rec* fstab);
+int fs_mgr_has_sysfs_path(const struct fstab_rec* fstab);
std::string fs_mgr_get_slot_suffix();
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 6b14289..6c8fecf 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -98,9 +98,6 @@
ifeq ($(strip $(BOARD_CHARGER_NO_UI)),true)
LOCAL_CHARGER_NO_UI := true
endif
-ifdef BRILLO
-LOCAL_CHARGER_NO_UI := true
-endif
LOCAL_SRC_FILES := \
healthd_common.cpp \
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 08b8b26..fa79d0b 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -143,7 +143,7 @@
BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) {
std::string buf;
- BatteryMonitor::PowerSupplyType ret;
+ int ret;
struct sysfsStringEnumMap supplyTypeMap[] = {
{ "Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN },
{ "Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY },
@@ -164,13 +164,13 @@
if (readFromFile(path, &buf) <= 0)
return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
- ret = (BatteryMonitor::PowerSupplyType)mapSysfsString(buf.c_str(), supplyTypeMap);
+ ret = mapSysfsString(buf.c_str(), supplyTypeMap);
if (ret < 0) {
KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf.c_str());
ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;
}
- return ret;
+ return static_cast<BatteryMonitor::PowerSupplyType>(ret);
}
bool BatteryMonitor::getBooleanField(const String8& path) {
diff --git a/init/Android.bp b/init/Android.bp
index 69b4ee4..31c8efb 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -194,4 +194,67 @@
static_libs: ["libinit"],
}
+// Host Verifier
+// ------------------------------------------------------------------------------
+
+genrule {
+ name: "generated_stub_builtin_function_map",
+ out: ["generated_stub_builtin_function_map.h"],
+ srcs: ["builtins.cpp"],
+ cmd: "sed -n '/Builtin-function-map start/{:a;n;/Builtin-function-map end/q;p;ba}' $(in) | sed -e 's/do_[^}]*/do_stub/g' > $(out)",
+}
+
+cc_binary {
+ name: "host_init_verifier",
+ host_supported: true,
+ cpp_std: "experimental",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Wno-unused-parameter",
+ "-Werror",
+ ],
+ static_libs: [
+ "libbase",
+ "libselinux",
+ ],
+ whole_static_libs: ["libcap"],
+ shared_libs: [
+ "libprotobuf-cpp-lite",
+ "libhidl-gen-utils",
+ "libprocessgroup",
+ "liblog",
+ "libcutils",
+ ],
+ srcs: [
+ "action.cpp",
+ "action_manager.cpp",
+ "action_parser.cpp",
+ "capabilities.cpp",
+ "descriptors.cpp",
+ "import_parser.cpp",
+ "host_init_parser.cpp",
+ "host_init_stubs.cpp",
+ "parser.cpp",
+ "rlimit_parser.cpp",
+ "tokenizer.cpp",
+ "service.cpp",
+ "subcontext.cpp",
+ "subcontext.proto",
+ "util.cpp",
+ ],
+ proto: {
+ type: "lite",
+ },
+ generated_headers: ["generated_stub_builtin_function_map"],
+ target: {
+ android: {
+ enabled: false,
+ },
+ darwin: {
+ enabled: false,
+ },
+ },
+}
+
subdirs = ["*"]
diff --git a/init/README.md b/init/README.md
index d7edf21..59ddd77 100644
--- a/init/README.md
+++ b/init/README.md
@@ -10,7 +10,11 @@
whitespace from breaking text into multiple tokens. The backslash,
when it is the last character on a line, may be used for line-folding.
-Lines which start with a # (leading whitespace allowed) are comments.
+Lines which start with a `#` (leading whitespace allowed) are comments.
+
+System properties can be expanded using the syntax
+`${property.name}`. This also works in contexts where concatenation is
+required, such as `import /init.recovery.${ro.hardware}.rc`.
Actions and Services implicitly declare a new section. All commands
or options belong to the section most recently declared. Commands
@@ -157,6 +161,25 @@
Options are modifiers to services. They affect how and when init
runs the service.
+`capabilities <capability> [ <capability>\* ]`
+> Set capabilities when exec'ing this service. 'capability' should be a Linux
+ capability without the "CAP\_" prefix, like "NET\_ADMIN" or "SETPCAP". See
+ http://man7.org/linux/man-pages/man7/capabilities.7.html for a list of Linux
+ capabilities.
+
+`class <name> [ <name>\* ]`
+> Specify class names for the service. All services in a
+ named class may be started or stopped together. A service
+ is in the class "default" if one is not specified via the
+ class option. Additional classnames beyond the (required) first
+ one are used to group services.
+ The `animation` class should include all services necessary for both
+ boot animation and shutdown animation. As these services can be
+ launched very early during bootup and can run until the last stage
+ of shutdown, access to /data partition is not guaranteed. These
+ services can check files under /data but it should not keep files opened
+ and should work when /data is not available.
+
`console [<console>]`
> This service needs a console. The optional second parameter chooses a
specific console instead of the default. The default "/dev/console" can
@@ -172,9 +195,93 @@
> This service will not automatically start with its class.
It must be explicitly started by name.
+`file <path> <type>`
+> Open a file path and pass its fd to the launched process. _type_ must be
+ "r", "w" or "rw". For native executables see libcutils
+ android\_get\_control\_file().
+
+`group <groupname> [ <groupname>\* ]`
+> Change to 'groupname' before exec'ing this service. Additional
+ groupnames beyond the (required) first one are used to set the
+ supplemental groups of the process (via setgroups()).
+ Currently defaults to root. (??? probably should default to nobody)
+
+`interface <interface name> <instance name>`
+> Associates this service with a list of the HIDL services that it provides. The interface name
+ must be a fully-qualified name and not a value name. This is used to allow hwservicemanager to
+ lazily start services.
+ For example: interface vendor.foo.bar@1.0::IBaz default
+
+`ioprio <class> <priority>`
+> Sets the IO priority and IO priority class for this service via the SYS_ioprio_set syscall.
+ _class_ must be one of "rt", "be", or "idle". _priority_ must be an integer in the range 0 - 7.
+
+`keycodes <keycode> [ <keycode>\* ]`
+> Sets the keycodes that will trigger this service. If all of the keys corresponding to the passed
+ keycodes are pressed at once, the service will start. This is typically used to start the
+ bugreport service.
+
+`memcg.limit_in_bytes <value>`
+> Sets the child's memory.limit_in_bytes to the specified value (only if memcg is mounted),
+ which must be equal or greater than 0.
+
+`memcg.soft_limit_in_bytes <value>`
+> Sets the child's memory.soft_limit_in_bytes to the specified value (only if memcg is mounted),
+ which must be equal or greater than 0.
+
+`memcg.swappiness <value>`
+> Sets the child's memory.swappiness to the specified value (only if memcg is mounted),
+ which must be equal or greater than 0.
+
+`namespace <pid|mnt>`
+> Enter a new PID or mount namespace when forking the service.
+
+`oneshot`
+> Do not restart the service when it exits.
+
+`onrestart`
+> Execute a Command (see below) when service restarts.
+
+`oom_score_adjust <value>`
+> Sets the child's /proc/self/oom\_score\_adj to the specified value,
+ which must range from -1000 to 1000.
+
+`override`
+> Indicates that this service definition is meant to override a previous definition for a service
+ with the same name. This is typically meant for services on /odm to override those defined on
+ /vendor. The last service definition that init parses with this keyword is the service definition
+ will use for this service. Pay close attention to the order in which init.rc files are parsed,
+ since it has some peculiarities for backwards compatibility reasons. The 'imports' section of
+ this file has more details on the order.
+
+`priority <priority>`
+> Scheduling priority of the service process. This value has to be in range
+ -20 to 19. Default priority is 0. Priority is set via setpriority().
+
+`rlimit <resource> <cur> <max>`
+> This applies the given rlimit to the service. rlimits are inherited by child
+ processes, so this effectively applies the given rlimit to the process tree
+ started by this service.
+ It is parsed similarly to the setrlimit command specified below.
+
+`seclabel <seclabel>`
+> Change to 'seclabel' before exec'ing this service.
+ Primarily for use by services run from the rootfs, e.g. ueventd, adbd.
+ Services on the system partition can instead use policy-defined transitions
+ based on their file security context.
+ If not specified and no transition is defined in policy, defaults to the init context.
+
`setenv <name> <value>`
> Set the environment variable _name_ to _value_ in the launched process.
+`shutdown <shutdown_behavior>`
+> Set shutdown behavior of the service process. When this is not specified,
+ the service is killed during shutdown process by using SIGTERM and SIGKILL.
+ The service with shutdown_behavior of "critical" is not killed during shutdown
+ until shutdown times out. When shutdown times out, even services tagged with
+ "shutdown critical" will be killed. When the service tagged with "shutdown critical"
+ is not running when shut down starts, it will be started.
+
`socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]`
> Create a unix domain socket named /dev/socket/_name_ and pass its fd to the
launched process. _type_ must be "dgram", "stream" or "seqpacket". User and
@@ -183,11 +290,6 @@
seclabel or computed based on the service executable file security context.
For native executables see libcutils android\_get\_control\_socket().
-`file <path> <type>`
-> Open a file path and pass its fd to the launched process. _type_ must be
- "r", "w" or "rw". For native executables see libcutils
- android\_get\_control\_file().
-
`user <username>`
> Change to 'username' before exec'ing this service.
Currently defaults to root. (??? probably should default to nobody)
@@ -204,88 +306,12 @@
As of Android O, processes can also request capabilities directly in their .rc
files. See the "capabilities" option below.
-`group <groupname> [ <groupname>\* ]`
-> Change to 'groupname' before exec'ing this service. Additional
- groupnames beyond the (required) first one are used to set the
- supplemental groups of the process (via setgroups()).
- Currently defaults to root. (??? probably should default to nobody)
-
-`capabilities <capability> [ <capability>\* ]`
-> Set capabilities when exec'ing this service. 'capability' should be a Linux
- capability without the "CAP\_" prefix, like "NET\_ADMIN" or "SETPCAP". See
- http://man7.org/linux/man-pages/man7/capabilities.7.html for a list of Linux
- capabilities.
-
-`setrlimit <resource> <cur> <max>`
-> This applies the given rlimit to the service. rlimits are inherited by child
- processes, so this effectively applies the given rlimit to the process tree
- started by this service.
- It is parsed similarly to the setrlimit command specified below.
-
-`seclabel <seclabel>`
-> Change to 'seclabel' before exec'ing this service.
- Primarily for use by services run from the rootfs, e.g. ueventd, adbd.
- Services on the system partition can instead use policy-defined transitions
- based on their file security context.
- If not specified and no transition is defined in policy, defaults to the init context.
-
-`oneshot`
-> Do not restart the service when it exits.
-
-`class <name> [ <name>\* ]`
-> Specify class names for the service. All services in a
- named class may be started or stopped together. A service
- is in the class "default" if one is not specified via the
- class option. Additional classnames beyond the (required) first
- one are used to group services.
-`animation class`
-> 'animation' class should include all services necessary for both
- boot animation and shutdown animation. As these services can be
- launched very early during bootup and can run until the last stage
- of shutdown, access to /data partition is not guaranteed. These
- services can check files under /data but it should not keep files opened
- and should work when /data is not available.
-
-`onrestart`
-> Execute a Command (see below) when service restarts.
-
`writepid <file> [ <file>\* ]`
> Write the child's pid to the given files when it forks. Meant for
cgroup/cpuset usage. If no files under /dev/cpuset/ are specified, but the
system property 'ro.cpuset.default' is set to a non-empty cpuset name (e.g.
'/foreground'), then the pid is written to file /dev/cpuset/_cpuset\_name_/tasks.
-`priority <priority>`
-> Scheduling priority of the service process. This value has to be in range
- -20 to 19. Default priority is 0. Priority is set via setpriority().
-
-`namespace <pid|mnt>`
-> Enter a new PID or mount namespace when forking the service.
-
-`oom_score_adjust <value>`
-> Sets the child's /proc/self/oom\_score\_adj to the specified value,
- which must range from -1000 to 1000.
-
-`memcg.swappiness <value>`
-> Sets the child's memory.swappiness to the specified value (only if memcg is mounted),
- which must be equal or greater than 0.
-
-`memcg.soft_limit_in_bytes <value>`
-> Sets the child's memory.soft_limit_in_bytes to the specified value (only if memcg is mounted),
- which must be equal or greater than 0.
-
-`memcg.limit_in_bytes <value>`
-> Sets the child's memory.limit_in_bytes to the specified value (only if memcg is mounted),
- which must be equal or greater than 0.
-
-`shutdown <shutdown_behavior>`
-> Set shutdown behavior of the service process. When this is not specified,
- the service is killed during shutdown process by using SIGTERM and SIGKILL.
- The service with shutdown_behavior of "critical" is not killed during shutdown
- until shutdown times out. When shutdown times out, even services tagged with
- "shutdown critical" will be killed. When the service tagged with "shutdown critical"
- is not running when shut down starts, it will be started.
-
Triggers
--------
diff --git a/init/action.cpp b/init/action.cpp
index 11335ca..f782b51 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -18,11 +18,16 @@
#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
-#include <android-base/properties.h>
#include <android-base/strings.h>
#include "util.h"
+#if defined(__ANDROID__)
+#include <android-base/properties.h>
+#else
+#include "host_init_stubs.h"
+#endif
+
using android::base::Join;
namespace android {
diff --git a/init/action_parser.cpp b/init/action_parser.cpp
index 8a4b518..a2c9671 100644
--- a/init/action_parser.cpp
+++ b/init/action_parser.cpp
@@ -16,11 +16,16 @@
#include "action_parser.h"
-#include <android-base/properties.h>
#include <android-base/strings.h>
#include "stable_properties.h"
+#if defined(__ANDROID__)
+#include <android-base/properties.h>
+#else
+#include "host_init_stubs.h"
+#endif
+
using android::base::GetBoolProperty;
using android::base::StartsWith;
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 1040b47..fc74dda 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -285,8 +285,11 @@
if (e4crypt_is_native()) {
if (e4crypt_set_directory_policy(args[1].c_str())) {
- reboot_into_recovery(
- {"--prompt_and_wipe_data", "--reason=set_policy_failed:"s + args[1]});
+ const std::vector<std::string> options = {
+ "--prompt_and_wipe_data",
+ "--reason=set_policy_failed:"s + args[1]};
+ reboot_into_recovery(options);
+ return Success();
}
}
return Success();
@@ -968,8 +971,8 @@
const char* value = args[2].c_str();
size_t value_len = strlen(value);
- if (!is_legal_property_name(name)) {
- return Error() << "is_legal_property_name(" << name << ") failed";
+ if (!IsLegalPropertyName(name)) {
+ return Error() << "IsLegalPropertyName(" << name << ") failed";
}
if (value_len >= PROP_VALUE_MAX) {
return Error() << "value too long";
@@ -984,24 +987,6 @@
return android::base::GetProperty("ro.crypto.type", "") == "file";
}
-static Result<Success> ExecWithRebootOnFailure(const std::string& reboot_reason,
- const std::vector<std::string>& args) {
- auto service = Service::MakeTemporaryOneshotService(args);
- if (!service) {
- return Error() << "Could not create exec service";
- }
- service->AddReapCallback([reboot_reason](const siginfo_t& siginfo) {
- if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) {
- reboot_into_recovery({"--prompt_and_wipe_data", "--reason="s + reboot_reason});
- }
- });
- if (auto result = service->ExecStart(); !result) {
- return Error() << "Could not start exec service: " << result.error();
- }
- ServiceList::GetInstance().AddService(std::move(service));
- return Success();
-}
-
static Result<Success> do_installkey(const BuiltinArguments& args) {
if (!is_file_crypto()) return Success();
@@ -1009,15 +994,18 @@
if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {
return ErrnoError() << "Failed to create " << unencrypted_dir;
}
- return ExecWithRebootOnFailure("enablefilecrypto_failed", {"exec", "/system/bin/vdc", "--wait",
- "cryptfs", "enablefilecrypto"});
+ std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
+ "enablefilecrypto"};
+ return do_exec({std::move(exec_args), args.context});
}
static Result<Success> do_init_user0(const BuiltinArguments& args) {
- return ExecWithRebootOnFailure("init_user0_failed",
- {"exec", "/system/bin/vdc", "--wait", "cryptfs", "init_user0"});
+ std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
+ "init_user0"};
+ return do_exec({std::move(exec_args), args.context});
}
+// Builtin-function-map start
const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
@@ -1075,6 +1063,7 @@
// clang-format on
return builtin_functions;
}
+// Builtin-function-map end
} // namespace init
} // namespace android
diff --git a/init/capabilities.cpp b/init/capabilities.cpp
index 50987db..a91cd1d 100644
--- a/init/capabilities.cpp
+++ b/init/capabilities.cpp
@@ -14,7 +14,6 @@
#include "capabilities.h"
-#include <sys/capability.h>
#include <sys/prctl.h>
#include <map>
@@ -72,10 +71,15 @@
static_assert(CAP_LAST_CAP == CAP_AUDIT_READ, "CAP_LAST_CAP is not CAP_AUDIT_READ");
static bool ComputeCapAmbientSupported() {
+#if defined(__ANDROID__)
return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) >= 0;
+#else
+ return true;
+#endif
}
static unsigned int ComputeLastValidCap() {
+#if defined(__ANDROID__)
// Android does not support kernels < 3.8. 'CAP_WAKE_ALARM' has been present since 3.0, see
// http://lxr.free-electrons.com/source/include/linux/capability.h?v=3.0#L360.
unsigned int last_valid_cap = CAP_WAKE_ALARM;
@@ -83,6 +87,9 @@
// |last_valid_cap| will be the first failing value.
return last_valid_cap - 1;
+#else
+ return CAP_LAST_CAP;
+#endif
}
static bool DropBoundingSet(const CapSet& to_keep) {
@@ -139,6 +146,7 @@
}
static bool SetAmbientCaps(const CapSet& to_raise) {
+#if defined(__ANDROID__)
for (size_t cap = 0; cap < to_raise.size(); ++cap) {
if (to_raise.test(cap)) {
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) != 0) {
@@ -147,6 +155,7 @@
}
}
}
+#endif
return true;
}
diff --git a/init/capabilities.h b/init/capabilities.h
index fc80c98..891e0ac 100644
--- a/init/capabilities.h
+++ b/init/capabilities.h
@@ -21,6 +21,17 @@
#include <string>
#include <type_traits>
+#if !defined(__ANDROID__)
+#ifndef CAP_BLOCK_SUSPEND
+#define CAP_BLOCK_SUSPEND 36
+#endif
+#ifndef CAP_AUDIT_READ
+#define CAP_AUDIT_READ 37
+#endif
+#undef CAP_LAST_CAP
+#define CAP_LAST_CAP CAP_AUDIT_READ
+#endif
+
namespace android {
namespace init {
diff --git a/init/host_init_parser.cpp b/init/host_init_parser.cpp
new file mode 100644
index 0000000..5232b7e
--- /dev/null
+++ b/init/host_init_parser.cpp
@@ -0,0 +1,82 @@
+//
+// 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 <pwd.h>
+
+#include <android-base/logging.h>
+
+#include "action.h"
+#include "action_manager.h"
+#include "action_parser.h"
+#include "parser.h"
+#include "result.h"
+#include "service.h"
+
+// The host passwd file won't have the Android entries, so we fake success here.
+passwd* getpwnam(const char* login) { // NOLINT: implementing bad function.
+ char dummy_buf[] = "dummy";
+ static passwd dummy_passwd = {
+ .pw_name = dummy_buf,
+ .pw_dir = dummy_buf,
+ .pw_shell = dummy_buf,
+ .pw_uid = 123,
+ .pw_gid = 123,
+ };
+ return &dummy_passwd;
+}
+
+namespace android {
+namespace init {
+
+static Result<Success> do_stub(const BuiltinArguments& args) {
+ return Success();
+}
+
+#include "generated_stub_builtin_function_map.h"
+
+int main(int argc, char** argv) {
+ android::base::InitLogging(argv, &android::base::StderrLogger);
+ if (argc != 2) {
+ LOG(ERROR) << "Usage: " << argv[0] << " <init file to parse>";
+ return -1;
+ }
+ const BuiltinFunctionMap function_map;
+ Action::set_function_map(&function_map);
+ ActionManager& am = ActionManager::GetInstance();
+ ServiceList& sl = ServiceList::GetInstance();
+ Parser parser;
+ parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sl, nullptr));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
+
+ size_t num_errors = 0;
+ if (!parser.ParseConfig(argv[1], &num_errors)) {
+ LOG(ERROR) << "Failed to find script";
+ return -1;
+ }
+ if (num_errors > 0) {
+ LOG(ERROR) << "Parse failed with " << num_errors << " errors";
+ return -1;
+ }
+ LOG(INFO) << "Parse success!";
+ return 0;
+}
+
+} // namespace init
+} // namespace android
+
+int main(int argc, char** argv) {
+ android::init::main(argc, argv);
+}
diff --git a/init/host_init_stubs.cpp b/init/host_init_stubs.cpp
new file mode 100644
index 0000000..e6cc08a
--- /dev/null
+++ b/init/host_init_stubs.cpp
@@ -0,0 +1,59 @@
+/*
+ * 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 "host_init_stubs.h"
+
+// unistd.h
+int setgroups(size_t __size, const gid_t* __list) {
+ return 0;
+}
+
+namespace android {
+namespace base {
+
+std::string GetProperty(const std::string&, const std::string& default_value) {
+ return default_value;
+}
+
+bool GetBoolProperty(const std::string&, bool default_value) {
+ return default_value;
+}
+
+} // namespace base
+} // namespace android
+
+namespace android {
+namespace init {
+
+// init.h
+std::string default_console = "/dev/console";
+
+// property_service.h
+uint32_t (*property_set)(const std::string& name, const std::string& value) = nullptr;
+uint32_t HandlePropertySet(const std::string&, const std::string&, const std::string&, const ucred&,
+ std::string*) {
+ return 0;
+}
+
+// selinux.h
+void SelabelInitialize() {}
+
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {
+ return false;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
new file mode 100644
index 0000000..f31ece6
--- /dev/null
+++ b/init/host_init_stubs.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#ifndef _INIT_HOST_INIT_STUBS_H
+#define _INIT_HOST_INIT_STUBS_H
+
+#include <stddef.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <string>
+
+// sys/system_properties.h
+#define PROP_VALUE_MAX 92
+
+// unistd.h
+int setgroups(size_t __size, const gid_t* __list);
+
+// android-base/properties.h
+namespace android {
+namespace base {
+
+std::string GetProperty(const std::string& key, const std::string& default_value);
+bool GetBoolProperty(const std::string& key, bool default_value);
+
+} // namespace base
+} // namespace android
+
+namespace android {
+namespace init {
+
+// init.h
+extern std::string default_console;
+
+// property_service.h
+extern uint32_t (*property_set)(const std::string& name, const std::string& value);
+uint32_t HandlePropertySet(const std::string& name, const std::string& value,
+ const std::string& source_context, const ucred& cr, std::string* error);
+
+// selinux.h
+void SelabelInitialize();
+bool SelabelLookupFileContext(const std::string& key, int type, std::string* result);
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/init.cpp b/init/init.cpp
index 7e4eaa8..efb9c1d 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -35,6 +35,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <cutils/android_reboot.h>
#include <keyutils.h>
@@ -63,7 +64,10 @@
using android::base::boot_clock;
using android::base::GetProperty;
+using android::base::ReadFileToString;
+using android::base::StringPrintf;
using android::base::Timer;
+using android::base::Trim;
namespace android {
namespace init {
@@ -246,7 +250,7 @@
return control_message_functions;
}
-void handle_control_message(const std::string& msg, const std::string& name) {
+void HandleControlMessage(const std::string& msg, const std::string& name, pid_t pid) {
const auto& map = get_control_message_map();
const auto it = map.find(msg);
@@ -255,6 +259,18 @@
return;
}
+ std::string cmdline_path = StringPrintf("proc/%d/cmdline", pid);
+ std::string process_cmdline;
+ if (ReadFileToString(cmdline_path, &process_cmdline)) {
+ std::replace(process_cmdline.begin(), process_cmdline.end(), '\0', ' ');
+ process_cmdline = Trim(process_cmdline);
+ } else {
+ process_cmdline = "unknown process";
+ }
+
+ LOG(INFO) << "Received control message '" << msg << "' for '" << name << "' from pid: " << pid
+ << " (" << process_cmdline << ")";
+
const ControlMessageFunction& function = it->second;
if (function.target == ControlTarget::SERVICE) {
diff --git a/init/init.h b/init/init.h
index ecce5d7..d4a0e96 100644
--- a/init/init.h
+++ b/init/init.h
@@ -17,6 +17,8 @@
#ifndef _INIT_INIT_H
#define _INIT_INIT_H
+#include <sys/types.h>
+
#include <string>
#include <vector>
@@ -36,7 +38,7 @@
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list);
-void handle_control_message(const std::string& msg, const std::string& arg);
+void HandleControlMessage(const std::string& msg, const std::string& arg, pid_t pid);
void property_changed(const std::string& name, const std::string& value);
diff --git a/init/parser.cpp b/init/parser.cpp
index 4c69bac..4453aaa 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -39,7 +39,7 @@
line_callbacks_.emplace_back(prefix, callback);
}
-void Parser::ParseData(const std::string& filename, const std::string& data) {
+void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
// TODO: Use a parser with const input and remove this copy
std::vector<char> data_copy(data.begin(), data.end());
data_copy.push_back('\0');
@@ -57,6 +57,7 @@
if (section_parser == nullptr) return;
if (auto result = section_parser->EndSection(); !result) {
+ (*parse_errors)++;
LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
}
@@ -80,6 +81,7 @@
end_section();
if (auto result = callback(std::move(args)); !result) {
+ (*parse_errors)++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
break;
@@ -92,12 +94,14 @@
if (auto result =
section_parser->ParseSection(std::move(args), filename, state.line);
!result) {
+ (*parse_errors)++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
section_parser = nullptr;
}
} else if (section_parser) {
if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
!result) {
+ (*parse_errors)++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
}
@@ -110,7 +114,7 @@
}
}
-bool Parser::ParseConfigFile(const std::string& path) {
+bool Parser::ParseConfigFile(const std::string& path, size_t* parse_errors) {
LOG(INFO) << "Parsing file " << path << "...";
android::base::Timer t;
auto config_contents = ReadFile(path);
@@ -120,7 +124,7 @@
}
config_contents->push_back('\n'); // TODO: fix parse_config.
- ParseData(path, *config_contents);
+ ParseData(path, *config_contents, parse_errors);
for (const auto& [section_name, section_parser] : section_parsers_) {
section_parser->EndFile();
}
@@ -129,7 +133,7 @@
return true;
}
-bool Parser::ParseConfigDir(const std::string& path) {
+bool Parser::ParseConfigDir(const std::string& path, size_t* parse_errors) {
LOG(INFO) << "Parsing directory " << path << "...";
std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);
if (!config_dir) {
@@ -149,7 +153,7 @@
// Sort first so we load files in a consistent order (bug 31996208)
std::sort(files.begin(), files.end());
for (const auto& file : files) {
- if (!ParseConfigFile(file)) {
+ if (!ParseConfigFile(file, parse_errors)) {
LOG(ERROR) << "could not import file '" << file << "'";
}
}
@@ -157,10 +161,16 @@
}
bool Parser::ParseConfig(const std::string& path) {
+ size_t parse_errors;
+ return ParseConfig(path, &parse_errors);
+}
+
+bool Parser::ParseConfig(const std::string& path, size_t* parse_errors) {
+ *parse_errors = 0;
if (is_dir(path.c_str())) {
- return ParseConfigDir(path);
+ return ParseConfigDir(path, parse_errors);
}
- return ParseConfigFile(path);
+ return ParseConfigFile(path, parse_errors);
}
} // namespace init
diff --git a/init/parser.h b/init/parser.h
index 110a468..f6e237f 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -72,13 +72,14 @@
Parser();
bool ParseConfig(const std::string& path);
+ bool ParseConfig(const std::string& path, size_t* parse_errors);
void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
void AddSingleLineParser(const std::string& prefix, LineCallback callback);
private:
- void ParseData(const std::string& filename, const std::string& data);
- bool ParseConfigFile(const std::string& path);
- bool ParseConfigDir(const std::string& path);
+ void ParseData(const std::string& filename, const std::string& data, size_t* parse_errors);
+ bool ParseConfigFile(const std::string& path, size_t* parse_errors);
+ bool ParseConfigDir(const std::string& path, size_t* parse_errors);
std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
std::vector<std::pair<std::string, LineCallback>> line_callbacks_;
diff --git a/init/property_service.cpp b/init/property_service.cpp
index ecd5baa..95ef35c 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -59,8 +59,11 @@
#include "init.h"
#include "persistent_properties.h"
#include "property_type.h"
+#include "subcontext.h"
#include "util.h"
+using namespace std::literals;
+
using android::base::ReadFileToString;
using android::base::Split;
using android::base::StartsWith;
@@ -117,48 +120,21 @@
return has_access;
}
-bool is_legal_property_name(const std::string& name) {
- size_t namelen = name.size();
-
- if (namelen < 1) return false;
- if (name[0] == '.') return false;
- if (name[namelen - 1] == '.') return false;
-
- /* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
- /* Don't allow ".." to appear in a property name */
- for (size_t i = 0; i < namelen; i++) {
- if (name[i] == '.') {
- // i=0 is guaranteed to never have a dot. See above.
- if (name[i-1] == '.') return false;
- continue;
- }
- if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
- if (name[i] >= 'a' && name[i] <= 'z') continue;
- if (name[i] >= 'A' && name[i] <= 'Z') continue;
- if (name[i] >= '0' && name[i] <= '9') continue;
- return false;
- }
-
- return true;
-}
-
-static uint32_t PropertySetImpl(const std::string& name, const std::string& value) {
+static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
size_t valuelen = value.size();
- if (!is_legal_property_name(name)) {
- LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: bad name";
+ if (!IsLegalPropertyName(name)) {
+ *error = "Illegal property name";
return PROP_ERROR_INVALID_NAME;
}
if (valuelen >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {
- LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
- << "value too long";
+ *error = "Property value too long";
return PROP_ERROR_INVALID_VALUE;
}
if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
- LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
- << "value not a UTF8 encoded string";
+ *error = "Value is not a UTF8 encoded string";
return PROP_ERROR_INVALID_VALUE;
}
@@ -166,8 +142,7 @@
if (pi != nullptr) {
// ro.* properties are actually "write-once".
if (StartsWith(name, "ro.")) {
- LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
- << "property already set";
+ *error = "Read-only property was already set";
return PROP_ERROR_READ_ONLY_PROPERTY;
}
@@ -175,8 +150,7 @@
} else {
int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
if (rc < 0) {
- LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
- << "__system_property_add failed";
+ *error = "__system_property_add failed";
return PROP_ERROR_SET_FAILED;
}
}
@@ -230,8 +204,10 @@
if (info.pid != pid) {
return false;
}
- if (PropertySetImpl(info.name, info.value) != PROP_SUCCESS) {
- LOG(ERROR) << "Failed to set async property " << info.name;
+ std::string error;
+ if (PropertySet(info.name, info.value, &error) != PROP_SUCCESS) {
+ LOG(ERROR) << "Failed to set async property " << info.name << " to " << info.value << ": "
+ << error;
}
property_children.pop();
if (!property_children.empty()) {
@@ -241,9 +217,9 @@
}
static uint32_t PropertySetAsync(const std::string& name, const std::string& value,
- PropertyAsyncFunc func) {
+ PropertyAsyncFunc func, std::string* error) {
if (value.empty()) {
- return PropertySetImpl(name, value);
+ return PropertySet(name, value, error);
}
PropertyChildInfo info;
@@ -261,30 +237,27 @@
return selinux_android_restorecon(value.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);
}
-uint32_t PropertySet(const std::string& name, const std::string& value) {
- if (name == "selinux.restorecon_recursive") {
- return PropertySetAsync(name, value, RestoreconRecursiveAsync);
- }
-
- return PropertySetImpl(name, value);
-}
-
uint32_t InitPropertySet(const std::string& name, const std::string& value) {
if (StartsWith(name, "ctl.")) {
- LOG(ERROR) << "Do not set ctl. properties from init; call the Service functions directly";
+ LOG(ERROR) << "InitPropertySet: Do not set ctl. properties from init; call the Service "
+ "functions directly";
+ return PROP_ERROR_INVALID_NAME;
+ }
+ if (name == "selinux.restorecon_recursive") {
+ LOG(ERROR) << "InitPropertySet: Do not set selinux.restorecon_recursive from init; use the "
+ "restorecon builtin directly";
return PROP_ERROR_INVALID_NAME;
}
- const char* type = nullptr;
- property_info_area->GetPropertyInfo(name.c_str(), nullptr, &type);
-
- if (type == nullptr || !CheckType(type, value)) {
- LOG(ERROR) << "property_set: name: '" << name << "' type check failed, type: '"
- << (type ?: "(null)") << "' value: '" << value << "'";
- return PROP_ERROR_INVALID_VALUE;
+ uint32_t result = 0;
+ ucred cr = {.pid = 1, .uid = 0, .gid = 0};
+ std::string error;
+ result = HandlePropertySet(name, value, kInitContext.c_str(), cr, &error);
+ if (result != PROP_SUCCESS) {
+ LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
}
- return PropertySet(name, value);
+ return result;
}
class SocketConnection {
@@ -415,9 +388,9 @@
// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
- const std::string& source_context, const ucred& cr) {
- if (!is_legal_property_name(name)) {
- LOG(ERROR) << "PropertySet: illegal property name \"" << name << "\"";
+ const std::string& source_context, const ucred& cr, std::string* error) {
+ if (!IsLegalPropertyName(name)) {
+ *error = "Illegal property name";
return PROP_ERROR_INVALID_NAME;
}
@@ -430,13 +403,11 @@
const char* type = nullptr;
property_info_area->GetPropertyInfo(control_string.c_str(), &target_context, &type);
if (!CheckMacPerms(control_string, target_context, source_context.c_str(), cr)) {
- LOG(ERROR) << "PropertySet: Unable to " << (name.c_str() + 4) << " service ctl ["
- << value << "]"
- << " uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid;
+ *error = StringPrintf("Unable to '%s' service %s", name.c_str() + 4, value.c_str());
return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
}
- handle_control_message(name.c_str() + 4, value.c_str());
+ HandleControlMessage(name.c_str() + 4, value, cr.pid);
return PROP_SUCCESS;
}
@@ -445,13 +416,13 @@
property_info_area->GetPropertyInfo(name.c_str(), &target_context, &type);
if (!CheckMacPerms(name, target_context, source_context.c_str(), cr)) {
- LOG(ERROR) << "PropertySet: permission denied uid:" << cr.uid << " name:" << name;
+ *error = "SELinux permission check failed";
return PROP_ERROR_PERMISSION_DENIED;
}
if (type == nullptr || !CheckType(type, value)) {
- LOG(ERROR) << "PropertySet: name: '" << name << "' type check failed, type: '"
- << (type ?: "(null)") << "' value: '" << value << "'";
+ *error = StringPrintf("Property type check failed, value doesn't match expected type '%s'",
+ (type ?: "(null)"));
return PROP_ERROR_INVALID_VALUE;
}
@@ -470,7 +441,11 @@
<< process_log_string;
}
- return PropertySet(name, value);
+ if (name == "selinux.restorecon_recursive") {
+ return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
+ }
+
+ return PropertySet(name, value, error);
}
static void handle_property_set_fd() {
@@ -513,7 +488,16 @@
prop_name[PROP_NAME_MAX-1] = 0;
prop_value[PROP_VALUE_MAX-1] = 0;
- HandlePropertySet(prop_value, prop_value, socket.source_context(), socket.cred());
+ const auto& cr = socket.cred();
+ std::string error;
+ uint32_t result =
+ HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
+ if (result != PROP_SUCCESS) {
+ LOG(ERROR) << "Unable to set property '" << prop_name << "' to '" << prop_value
+ << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
+ << error;
+ }
+
break;
}
@@ -527,7 +511,14 @@
return;
}
- auto result = HandlePropertySet(name, value, socket.source_context(), socket.cred());
+ const auto& cr = socket.cred();
+ std::string error;
+ uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error);
+ if (result != PROP_SUCCESS) {
+ LOG(ERROR) << "Unable to set property '" << name << "' to '" << value
+ << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
+ << error;
+ }
socket.SendUint32(result);
break;
}
@@ -545,11 +536,17 @@
* Filter is used to decide which properties to load: NULL loads all keys,
* "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
*/
-static void load_properties(char *data, const char *filter)
-{
+static void LoadProperties(char* data, const char* filter, const char* filename) {
char *key, *value, *eol, *sol, *tmp, *fn;
size_t flen = 0;
+ const char* context = kInitContext.c_str();
+ for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
+ if (StartsWith(filename, path_prefix)) {
+ context = secontext;
+ }
+ }
+
if (filter) {
flen = strlen(filter);
}
@@ -596,7 +593,21 @@
}
}
- property_set(key, value);
+ if (StartsWith(key, "ctl.") || key == "sys.powerctl"s ||
+ key == "selinux.restorecon_recursive"s) {
+ LOG(ERROR) << "Ignoring disallowed property '" << key
+ << "' with special meaning in prop file '" << filename << "'";
+ continue;
+ }
+
+ uint32_t result = 0;
+ ucred cr = {.pid = 1, .uid = 0, .gid = 0};
+ std::string error;
+ result = HandlePropertySet(key, value, context, cr, &error);
+ if (result != PROP_SUCCESS) {
+ LOG(ERROR) << "Unable to set property '" << key << "' to '" << value
+ << "' in property file '" << filename << "': " << error;
+ }
}
}
}
@@ -612,7 +623,8 @@
return false;
}
file_contents->push_back('\n');
- load_properties(file_contents->data(), filter);
+
+ LoadProperties(file_contents->data(), filter, filename);
LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
return true;
}
diff --git a/init/property_service.h b/init/property_service.h
index 8161b40..29eaaa9 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -32,7 +32,7 @@
extern uint32_t (*property_set)(const std::string& name, const std::string& value);
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
- const std::string& source_context, const ucred& cr);
+ const std::string& source_context, const ucred& cr, std::string* error);
extern bool PropertyChildReap(pid_t pid);
@@ -41,7 +41,6 @@
void load_persist_props(void);
void load_system_props(void);
void start_property_service(void);
-bool is_legal_property_name(const std::string& name);
} // namespace init
} // namespace android
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 242750a..bb87f12 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -20,6 +20,7 @@
#include <fcntl.h>
#include <linux/fs.h>
#include <mntent.h>
+#include <semaphore.h>
#include <sys/capability.h>
#include <sys/cdefs.h>
#include <sys/ioctl.h>
@@ -89,12 +90,13 @@
mnt_opts_(entry.mnt_opts) {}
bool Umount(bool force) {
+ LOG(INFO) << "Unmounting " << mnt_fsname_ << ":" << mnt_dir_ << " opts " << mnt_opts_;
int r = umount2(mnt_dir_.c_str(), force ? MNT_FORCE : 0);
if (r == 0) {
- LOG(INFO) << "umounted " << mnt_fsname_ << ":" << mnt_dir_ << " opts " << mnt_opts_;
+ LOG(INFO) << "Umounted " << mnt_fsname_ << ":" << mnt_dir_ << " opts " << mnt_opts_;
return true;
} else {
- PLOG(WARNING) << "cannot umount " << mnt_fsname_ << ":" << mnt_dir_ << " opts "
+ PLOG(WARNING) << "Cannot umount " << mnt_fsname_ << ":" << mnt_dir_ << " opts "
<< mnt_opts_;
return false;
}
@@ -328,39 +330,9 @@
return stat;
}
-void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
- bool runFsck) {
+void RebootThread(unsigned int cmd, std::chrono::milliseconds shutdown_timeout, bool runFsck,
+ sem_t* reboot_semaphore) {
Timer t;
- LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
-
- // Ensure last reboot reason is reduced to canonical
- // alias reported in bootloader or system boot reason.
- size_t skip = 0;
- std::vector<std::string> reasons = Split(reason, ",");
- if (reasons.size() >= 2 && reasons[0] == "reboot" &&
- (reasons[1] == "recovery" || reasons[1] == "bootloader" || reasons[1] == "cold" ||
- reasons[1] == "hard" || reasons[1] == "warm")) {
- skip = strlen("reboot,");
- }
- property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str() + skip);
- sync();
-
- bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;
-
- auto shutdown_timeout = 0ms;
- if (!SHUTDOWN_ZERO_TIMEOUT) {
- if (is_thermal_shutdown) {
- constexpr unsigned int thermal_shutdown_timeout = 1;
- shutdown_timeout = std::chrono::seconds(thermal_shutdown_timeout);
- } else {
- constexpr unsigned int shutdown_timeout_default = 6;
- auto shutdown_timeout_property = android::base::GetUintProperty(
- "ro.build.shutdown_timeout", shutdown_timeout_default);
- shutdown_timeout = std::chrono::seconds(shutdown_timeout_property);
- }
- }
- LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms";
-
// keep debugging tools until non critical ones are all gone.
const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
// watchdogd is a vendor specific component but should be alive to complete shutdown safely.
@@ -384,7 +356,7 @@
}
// remaining operations (specifically fsck) may take a substantial duration
- if (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown) {
+ if (cmd == ANDROID_RB_POWEROFF || cmd == ANDROID_RB_THERMOFF) {
TurnOffBacklight();
}
@@ -457,12 +429,91 @@
if (kill_after_apps.count(s->name())) s->Stop();
}
// 4. sync, try umount, and optionally run fsck for user shutdown
- sync();
+ {
+ Timer sync_timer;
+ LOG(INFO) << "sync() before umount...";
+ sync();
+ LOG(INFO) << "sync() before umount took" << sync_timer;
+ }
UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
// Follow what linux shutdown is doing: one more sync with little bit delay
- sync();
- if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
+ {
+ Timer sync_timer;
+ LOG(INFO) << "sync() after umount...";
+ sync();
+ LOG(INFO) << "sync() after umount took" << sync_timer;
+ }
+ if (cmd != ANDROID_RB_THERMOFF) std::this_thread::sleep_for(100ms);
LogShutdownTime(stat, &t);
+
+ if (reboot_semaphore != nullptr) {
+ sem_post(reboot_semaphore);
+ }
+}
+
+void RunRebootThread(unsigned int cmd, std::chrono::milliseconds shutdown_timeout) {
+ sem_t reboot_semaphore;
+ timespec shutdown_timeout_timespec;
+
+ if (sem_init(&reboot_semaphore, false, 0) == -1 ||
+ clock_gettime(CLOCK_REALTIME, &shutdown_timeout_timespec) == -1) {
+ // These should never fail, but if they do, skip the graceful reboot and reboot immediately.
+ return;
+ }
+
+ std::thread reboot_thread(&RebootThread, cmd, shutdown_timeout, false, &reboot_semaphore);
+ reboot_thread.detach();
+
+ // One extra second than the timeout passed to the thread as there is a final Umount pass
+ // after the timeout is reached.
+ shutdown_timeout_timespec.tv_sec += 1 + shutdown_timeout.count() / 1000;
+
+ int sem_return = 0;
+ while ((sem_return = sem_timedwait(&reboot_semaphore, &shutdown_timeout_timespec)) == -1 &&
+ errno == EINTR) {
+ }
+
+ if (sem_return == -1) {
+ LOG(ERROR) << "Reboot thread timed out";
+ }
+}
+
+void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
+ bool runFsck) {
+ LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
+
+ // Ensure last reboot reason is reduced to canonical
+ // alias reported in bootloader or system boot reason.
+ size_t skip = 0;
+ std::vector<std::string> reasons = Split(reason, ",");
+ if (reasons.size() >= 2 && reasons[0] == "reboot" &&
+ (reasons[1] == "recovery" || reasons[1] == "bootloader" || reasons[1] == "cold" ||
+ reasons[1] == "hard" || reasons[1] == "warm")) {
+ skip = strlen("reboot,");
+ }
+ property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str() + skip);
+ sync();
+
+ auto shutdown_timeout = 0ms;
+ if (!SHUTDOWN_ZERO_TIMEOUT) {
+ if (cmd == ANDROID_RB_THERMOFF) {
+ constexpr auto kThermalShutdownTimeout = 1s;
+ shutdown_timeout = kThermalShutdownTimeout;
+ } else {
+ constexpr unsigned int kShutdownTimeoutDefault = 6;
+ auto shutdown_timeout_property = android::base::GetUintProperty(
+ "ro.build.shutdown_timeout", kShutdownTimeoutDefault);
+ shutdown_timeout = std::chrono::seconds(shutdown_timeout_property);
+ }
+ }
+ LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms";
+
+ if (runFsck) {
+ RebootThread(cmd, shutdown_timeout, true, nullptr);
+ } else {
+ RunRebootThread(cmd, shutdown_timeout);
+ }
+
// Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
RebootSystem(cmd, rebootTarget);
abort();
diff --git a/init/service.cpp b/init/service.cpp
index 35dd319..694e5e7 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -24,7 +24,6 @@
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/stat.h>
-#include <sys/system_properties.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <termios.h>
@@ -33,8 +32,6 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
-#include <android-base/properties.h>
-#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <hidl-util/FQName.h>
@@ -42,15 +39,23 @@
#include <selinux/selinux.h>
#include <system/thread_defs.h>
-#include "init.h"
-#include "property_service.h"
#include "rlimit_parser.h"
#include "util.h"
+#if defined(__ANDROID__)
+#include <sys/system_properties.h>
+
+#include <android-base/properties.h>
+
+#include "init.h"
+#include "property_service.h"
+#else
+#include "host_init_stubs.h"
+#endif
+
using android::base::boot_clock;
using android::base::GetProperty;
using android::base::Join;
-using android::base::make_scope_guard;
using android::base::ParseInt;
using android::base::StartsWith;
using android::base::StringPrintf;
@@ -298,7 +303,7 @@
}
}
-void Service::Reap(const siginfo_t& siginfo) {
+void Service::Reap() {
if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
KillProcessGroup(SIGKILL);
}
@@ -307,10 +312,6 @@
std::for_each(descriptors_.begin(), descriptors_.end(),
std::bind(&DescriptorInfo::Clean, std::placeholders::_1));
- for (const auto& f : reap_callbacks_) {
- f(siginfo);
- }
-
if (flags_ & SVC_EXEC) UnSetExec();
if (flags_ & SVC_TEMPORARY) return;
@@ -445,8 +446,8 @@
const std::string& interface_name = args[1];
const std::string& instance_name = args[2];
- const FQName fq_name = FQName(interface_name);
- if (!fq_name.isValid()) {
+ FQName fq_name;
+ if (!FQName::parse(interface_name, &fq_name)) {
return Error() << "Invalid fully-qualified name for interface '" << interface_name << "'";
}
@@ -677,29 +678,29 @@
{"console", {0, 1, &Service::ParseConsole}},
{"critical", {0, 0, &Service::ParseCritical}},
{"disabled", {0, 0, &Service::ParseDisabled}},
+ {"file", {2, 2, &Service::ParseFile}},
{"group", {1, NR_SVC_SUPP_GIDS + 1, &Service::ParseGroup}},
{"interface", {2, 2, &Service::ParseInterface}},
{"ioprio", {2, 2, &Service::ParseIoprio}},
- {"priority", {1, 1, &Service::ParsePriority}},
{"keycodes", {1, kMax, &Service::ParseKeycodes}},
- {"oneshot", {0, 0, &Service::ParseOneshot}},
- {"onrestart", {1, kMax, &Service::ParseOnrestart}},
- {"override", {0, 0, &Service::ParseOverride}},
- {"oom_score_adjust",
- {1, 1, &Service::ParseOomScoreAdjust}},
- {"memcg.swappiness",
- {1, 1, &Service::ParseMemcgSwappiness}},
- {"memcg.soft_limit_in_bytes",
- {1, 1, &Service::ParseMemcgSoftLimitInBytes}},
{"memcg.limit_in_bytes",
{1, 1, &Service::ParseMemcgLimitInBytes}},
+ {"memcg.soft_limit_in_bytes",
+ {1, 1, &Service::ParseMemcgSoftLimitInBytes}},
+ {"memcg.swappiness",
+ {1, 1, &Service::ParseMemcgSwappiness}},
{"namespace", {1, 2, &Service::ParseNamespace}},
+ {"oneshot", {0, 0, &Service::ParseOneshot}},
+ {"onrestart", {1, kMax, &Service::ParseOnrestart}},
+ {"oom_score_adjust",
+ {1, 1, &Service::ParseOomScoreAdjust}},
+ {"override", {0, 0, &Service::ParseOverride}},
+ {"priority", {1, 1, &Service::ParsePriority}},
{"rlimit", {3, 3, &Service::ParseProcessRlimit}},
{"seclabel", {1, 1, &Service::ParseSeclabel}},
{"setenv", {2, 2, &Service::ParseSetenv}},
{"shutdown", {1, 1, &Service::ParseShutdown}},
{"socket", {3, 6, &Service::ParseSocket}},
- {"file", {2, 2, &Service::ParseFile}},
{"user", {1, 1, &Service::ParseUser}},
{"writepid", {1, kMax, &Service::ParseWritepid}},
};
@@ -1168,7 +1169,7 @@
// Property values can contain any characters, but may only be a certain length.
// (The latter restriction is needed because `start` and `stop` work by writing
// the service name to the "ctl.start" and "ctl.stop" properties.)
- return is_legal_property_name("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
+ return IsLegalPropertyName("init.svc." + name) && name.size() <= PROP_VALUE_MAX;
}
} // namespace init
diff --git a/init/service.h b/init/service.h
index bcf1943..d46a413 100644
--- a/init/service.h
+++ b/init/service.h
@@ -17,7 +17,6 @@
#ifndef _INIT_SERVICE_H
#define _INIT_SERVICE_H
-#include <signal.h>
#include <sys/resource.h>
#include <sys/types.h>
@@ -82,7 +81,7 @@
void Stop();
void Terminate();
void Restart();
- void Reap(const siginfo_t& siginfo);
+ void Reap();
void DumpState() const;
void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; }
bool IsShutdownCritical() const { return (flags_ & SVC_SHUTDOWN_CRITICAL) != 0; }
@@ -90,9 +89,6 @@
is_exec_service_running_ = false;
flags_ &= ~SVC_EXEC;
}
- void AddReapCallback(std::function<void(const siginfo_t& siginfo)> callback) {
- reap_callbacks_.emplace_back(std::move(callback));
- }
static bool is_exec_service_running() { return is_exec_service_running_; }
@@ -214,8 +210,6 @@
std::vector<std::pair<int, rlimit>> rlimits_;
std::vector<std::string> args_;
-
- std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
};
class ServiceList {
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index badacaf..072a0fb 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -84,15 +84,16 @@
}
}
- if (siginfo.si_code == CLD_EXITED) {
- LOG(INFO) << name << " exited with status " << siginfo.si_status << wait_string;
- } else {
- LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;
+ auto status = siginfo.si_status;
+ if (WIFEXITED(status)) {
+ LOG(INFO) << name << " exited with status " << WEXITSTATUS(status) << wait_string;
+ } else if (WIFSIGNALED(status)) {
+ LOG(INFO) << name << " killed by signal " << WTERMSIG(status) << wait_string;
}
if (!service) return true;
- service->Reap(siginfo);
+ service->Reap();
if (service->flags() & SVC_TEMPORARY) {
ServiceList::GetInstance().RemoveService(*service);
diff --git a/init/stable_properties.h b/init/stable_properties.h
index be35457..bd568f0 100644
--- a/init/stable_properties.h
+++ b/init/stable_properties.h
@@ -29,6 +29,7 @@
};
static const std::set<std::string> kExportedActionableProperties = {
+ "init.svc.console",
"init.svc.mediadrm",
"init.svc.zygote",
"persist.bluetooth.btsnoopenable",
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index f3b643a..c1846f7 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -27,12 +27,14 @@
#include <selinux/android.h>
#include "action.h"
-#include "property_service.h"
-#include "selinux.h"
#include "util.h"
-#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
-#include <sys/_system_properties.h>
+#if defined(__ANDROID__)
+#include "property_service.h"
+#include "selinux.h"
+#else
+#include "host_init_stubs.h"
+#endif
using android::base::GetExecutablePath;
using android::base::Join;
@@ -47,6 +49,11 @@
const std::string kInitContext = "u:r:init:s0";
const std::string kVendorContext = "u:r:vendor_init:s0";
+const char* const paths_and_secontexts[2][2] = {
+ {"/vendor", kVendorContext.c_str()},
+ {"/odm", kVendorContext.c_str()},
+};
+
namespace {
constexpr size_t kBufferSize = 4096;
@@ -83,7 +90,7 @@
uint32_t SubcontextPropertySet(const std::string& name, const std::string& value) {
properties_to_set.emplace_back(name, value);
- return PROP_SUCCESS;
+ return 0;
}
class SubcontextProcess {
@@ -295,7 +302,11 @@
for (const auto& property : subcontext_reply->properties_to_set()) {
ucred cr = {.pid = pid_, .uid = 0, .gid = 0};
- HandlePropertySet(property.name(), property.value(), context_, cr);
+ std::string error;
+ if (HandlePropertySet(property.name(), property.value(), context_, cr, &error) != 0) {
+ LOG(ERROR) << "Subcontext init could not set '" << property.name() << "' to '"
+ << property.value() << "': " << error;
+ }
}
if (subcontext_reply->reply_case() == SubcontextReply::kFailure) {
@@ -343,9 +354,6 @@
static std::vector<Subcontext> subcontexts;
std::vector<Subcontext>* InitializeSubcontexts() {
- static const char* const paths_and_secontexts[][2] = {
- {"/vendor", kVendorContext.c_str()},
- };
for (const auto& [path_prefix, secontext] : paths_and_secontexts) {
subcontexts.emplace_back(path_prefix, secontext);
}
diff --git a/init/subcontext.h b/init/subcontext.h
index 5601b80..22d7d43 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -33,6 +33,7 @@
extern const std::string kInitContext;
extern const std::string kVendorContext;
+extern const char* const paths_and_secontexts[2][2];
class Subcontext {
public:
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index ac1d7c7..24b14c4 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -83,8 +83,8 @@
}
UeventListener::UeventListener() {
- // is 256K enough? udev uses 16MB!
- device_fd_.reset(uevent_open_socket(256 * 1024, true));
+ // is 2MB enough? udev uses 128MB!
+ device_fd_.reset(uevent_open_socket(2 * 1024 * 1024, true));
if (device_fd_ == -1) {
LOG(FATAL) << "Could not open uevent socket";
}
diff --git a/init/util.cpp b/init/util.cpp
index d80cb1e..4455b2e 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -33,7 +33,6 @@
#include <android-base/file.h>
#include <android-base/logging.h>
-#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
@@ -42,7 +41,14 @@
#include <selinux/android.h>
#include "reboot.h"
+
+#if defined(__ANDROID__)
+#include <android-base/properties.h>
+
#include "selinux.h"
+#else
+#include "host_init_stubs.h"
+#endif
#ifdef _INIT_INIT_H
#error "Do not include init.h in files used by ueventd or watchdogd; it will expose init's globals"
@@ -409,5 +415,30 @@
return false;
}
+bool IsLegalPropertyName(const std::string& name) {
+ size_t namelen = name.size();
+
+ if (namelen < 1) return false;
+ if (name[0] == '.') return false;
+ if (name[namelen - 1] == '.') return false;
+
+ /* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */
+ /* Don't allow ".." to appear in a property name */
+ for (size_t i = 0; i < namelen; i++) {
+ if (name[i] == '.') {
+ // i=0 is guaranteed to never have a dot. See above.
+ if (name[i - 1] == '.') return false;
+ continue;
+ }
+ if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;
+ if (name[i] >= 'a' && name[i] <= 'z') continue;
+ if (name[i] >= 'A' && name[i] <= 'Z') continue;
+ if (name[i] >= '0' && name[i] <= '9') continue;
+ return false;
+ }
+
+ return true;
+}
+
} // namespace init
} // namespace android
diff --git a/init/util.h b/init/util.h
index 2cfcf6c..07e4864 100644
--- a/init/util.h
+++ b/init/util.h
@@ -62,6 +62,8 @@
bool read_android_dt_file(const std::string& sub_path, std::string* dt_content);
bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
+bool IsLegalPropertyName(const std::string& name);
+
} // namespace init
} // namespace android
diff --git a/libasyncio/AsyncIO.cpp b/libasyncio/AsyncIO.cpp
index 7430bc8..6149f09 100644
--- a/libasyncio/AsyncIO.cpp
+++ b/libasyncio/AsyncIO.cpp
@@ -17,9 +17,10 @@
#include <asyncio/AsyncIO.h>
#include <sys/syscall.h>
#include <unistd.h>
+#include <cstdint>
+#include <cstring>
int io_setup(unsigned nr, aio_context_t* ctxp) {
- memset(ctxp, 0, sizeof(*ctxp));
return syscall(__NR_io_setup, nr, ctxp);
}
@@ -48,3 +49,11 @@
iocb->aio_nbytes = count;
iocb->aio_offset = offset;
}
+
+void io_prep_pread(struct iocb* iocb, int fd, void* buf, size_t count, long long offset) {
+ io_prep(iocb, fd, buf, count, offset, true);
+}
+
+void io_prep_pwrite(struct iocb* iocb, int fd, void* buf, size_t count, long long offset) {
+ io_prep(iocb, fd, buf, count, offset, false);
+}
diff --git a/libasyncio/include/asyncio/AsyncIO.h b/libasyncio/include/asyncio/AsyncIO.h
index e3fb93a..9620d2a 100644
--- a/libasyncio/include/asyncio/AsyncIO.h
+++ b/libasyncio/include/asyncio/AsyncIO.h
@@ -17,9 +17,9 @@
#ifndef _ASYNCIO_H
#define _ASYNCIO_H
-#include <cstring>
-#include <cstdint>
#include <linux/aio_abi.h>
+#include <stdbool.h>
+#include <stdint.h>
#include <sys/cdefs.h>
#include <sys/types.h>
#include <time.h>
@@ -35,10 +35,14 @@
int io_setup(unsigned nr, aio_context_t* ctxp);
int io_destroy(aio_context_t ctx);
-int io_submit(aio_context_t ctx, long nr, iocb** iocbpp);
-int io_getevents(aio_context_t ctx, long min_nr, long max_nr, io_event* events, timespec* timeout);
-int io_cancel(aio_context_t ctx, iocb*, io_event* result);
-void io_prep(iocb* iocb, int fd, const void* buf, uint64_t count, int64_t offset, bool read);
+int io_submit(aio_context_t ctx, long nr, struct iocb** iocbpp);
+int io_getevents(aio_context_t ctx, long min_nr, long max_nr, struct io_event* events,
+ struct timespec* timeout);
+int io_cancel(aio_context_t ctx, struct iocb*, struct io_event* result);
+
+void io_prep_pread(struct iocb* iocb, int fd, void* buf, size_t count, long long offset);
+void io_prep_pwrite(struct iocb* iocb, int fd, void* buf, size_t count, long long offset);
+void io_prep(struct iocb* iocb, int fd, const void* buf, uint64_t count, int64_t offset, bool read);
#ifdef __cplusplus
};
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 0e32e47..711a12a 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -128,6 +128,22 @@
return true;
}
+bool Backtrace::UnwindOffline(unwindstack::Regs* regs, BacktraceMap* back_map,
+ const backtrace_stackinfo_t& stack,
+ std::vector<backtrace_frame_data_t>* frames,
+ BacktraceUnwindError* error) {
+ UnwindStackOfflineMap* offline_map = reinterpret_cast<UnwindStackOfflineMap*>(back_map);
+ // Create the process memory from the stack data since this will almost
+ // always be different each unwind.
+ if (!offline_map->CreateProcessMemory(stack)) {
+ if (error != nullptr) {
+ error->error_code = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
+ }
+ return false;
+ }
+ return Backtrace::Unwind(regs, back_map, frames, 0U, nullptr, error);
+}
+
UnwindStackCurrent::UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map)
: BacktraceCurrent(pid, tid, map) {}
@@ -221,12 +237,12 @@
Backtrace* Backtrace::CreateOffline(ArchEnum arch, pid_t pid, pid_t tid,
const std::vector<backtrace_map_t>& maps,
const backtrace_stackinfo_t& stack) {
- BacktraceMap* map = BacktraceMap::CreateOffline(pid, maps, stack);
- if (map == nullptr) {
+ std::unique_ptr<UnwindStackOfflineMap> map(
+ reinterpret_cast<UnwindStackOfflineMap*>(BacktraceMap::CreateOffline(pid, maps)));
+ if (map.get() == nullptr || !map->CreateProcessMemory(stack)) {
return nullptr;
}
-
- return new UnwindStackOffline(arch, pid, tid, map, false);
+ return new UnwindStackOffline(arch, pid, tid, map.release(), false);
}
Backtrace* Backtrace::CreateOffline(ArchEnum arch, pid_t pid, pid_t tid, BacktraceMap* map) {
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 6dcc621..9c6fed4 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -127,12 +127,7 @@
return false;
}
-bool UnwindStackOfflineMap::Build(const std::vector<backtrace_map_t>& backtrace_maps,
- const backtrace_stackinfo_t& stack) {
- if (stack.start >= stack.end) {
- return false;
- }
-
+bool UnwindStackOfflineMap::Build(const std::vector<backtrace_map_t>& backtrace_maps) {
for (const backtrace_map_t& map : backtrace_maps) {
maps_.push_back(map);
}
@@ -145,6 +140,13 @@
for (const backtrace_map_t& map : maps_) {
maps->Add(map.start, map.end, map.offset, map.flags, map.name, map.load_bias);
}
+ return true;
+}
+
+bool UnwindStackOfflineMap::CreateProcessMemory(const backtrace_stackinfo_t& stack) {
+ if (stack.start >= stack.end) {
+ return false;
+ }
// Create the process memory from the stack data.
uint64_t size = stack.end - stack.start;
@@ -154,7 +156,6 @@
std::shared_ptr<unwindstack::Memory> shared_memory(memory);
process_memory_.reset(new unwindstack::MemoryRange(shared_memory, 0, size, stack.start));
-
return true;
}
@@ -182,10 +183,9 @@
//-------------------------------------------------------------------------
// BacktraceMap create offline function.
//-------------------------------------------------------------------------
-BacktraceMap* BacktraceMap::CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps,
- const backtrace_stackinfo_t& stack) {
+BacktraceMap* BacktraceMap::CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps) {
UnwindStackOfflineMap* map = new UnwindStackOfflineMap(pid);
- if (!map->Build(maps, stack)) {
+ if (!map->Build(maps)) {
delete map;
return nullptr;
}
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
index 94cbfb2..ec0d9c1 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -76,7 +76,9 @@
bool Build() override;
- bool Build(const std::vector<backtrace_map_t>& maps, const backtrace_stackinfo_t& stack);
+ bool Build(const std::vector<backtrace_map_t>& maps);
+
+ bool CreateProcessMemory(const backtrace_stackinfo_t& stack);
};
#endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index 7a37015..a088207 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -151,6 +151,11 @@
std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames,
std::vector<std::string>* skip_names, BacktraceUnwindError* error = nullptr);
+ static bool UnwindOffline(unwindstack::Regs* regs, BacktraceMap* back_map,
+ const backtrace_stackinfo_t& stack_info,
+ std::vector<backtrace_frame_data_t>* frames,
+ BacktraceUnwindError* error = nullptr);
+
// Get the function name and offset into the function given the pc.
// If the string is empty, then no valid function name was found,
// or the pc is not in any valid map.
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index da54472..473d195 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -64,8 +64,7 @@
// is unsupported.
static BacktraceMap* Create(pid_t pid, bool uncached = false);
- static BacktraceMap* CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps,
- const backtrace_stackinfo_t& stack);
+ static BacktraceMap* CreateOffline(pid_t pid, const std::vector<backtrace_map_t>& maps);
virtual ~BacktraceMap();
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index a993d41..5ea981f 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -202,16 +202,6 @@
CAP_MASK_LONG(CAP_NET_RAW),
"vendor/bin/hw/android.hardware.wifi@1.0-service" },
- // A non-privileged zygote that spawns isolated processes for web rendering.
- { 0750, AID_ROOT, AID_ROOT, CAP_MASK_LONG(CAP_SETUID) |
- CAP_MASK_LONG(CAP_SETGID) |
- CAP_MASK_LONG(CAP_SETPCAP),
- "system/bin/webview_zygote32" },
- { 0750, AID_ROOT, AID_ROOT, CAP_MASK_LONG(CAP_SETUID) |
- CAP_MASK_LONG(CAP_SETGID) |
- CAP_MASK_LONG(CAP_SETPCAP),
- "system/bin/webview_zygote64" },
-
// generic defaults
{ 00755, AID_ROOT, AID_ROOT, 0, "bin/*" },
{ 00640, AID_ROOT, AID_SHELL, 0, "fstab.*" },
diff --git a/libion/ion.c b/libion/ion.c
index 5836128..b8de5a4 100644
--- a/libion/ion.c
+++ b/libion/ion.c
@@ -55,7 +55,7 @@
int ion_open() {
int fd = open("/dev/ion", O_RDONLY | O_CLOEXEC);
- if (fd < 0) ALOGE("open /dev/ion failed!\n");
+ if (fd < 0) ALOGE("open /dev/ion failed: %s", strerror(errno));
return fd;
}
@@ -69,7 +69,7 @@
static int ion_ioctl(int fd, int req, void* arg) {
int ret = ioctl(fd, req, arg);
if (ret < 0) {
- ALOGE("ioctl %x failed with code %d: %s\n", req, ret, strerror(errno));
+ ALOGE("ioctl %x failed with code %d: %s", req, ret, strerror(errno));
return -errno;
}
return ret;
@@ -115,12 +115,12 @@
ret = ion_ioctl(fd, ION_IOC_MAP, &data);
if (ret < 0) return ret;
if (data.fd < 0) {
- ALOGE("map ioctl returned negative fd\n");
+ ALOGE("map ioctl returned negative fd");
return -EINVAL;
}
tmp_ptr = mmap(NULL, length, prot, flags, data.fd, offset);
if (tmp_ptr == MAP_FAILED) {
- ALOGE("mmap failed: %s\n", strerror(errno));
+ ALOGE("mmap failed: %s", strerror(errno));
return -errno;
}
*map_fd = data.fd;
@@ -140,7 +140,7 @@
ret = ion_ioctl(fd, ION_IOC_SHARE, &data);
if (ret < 0) return ret;
if (data.fd < 0) {
- ALOGE("share ioctl returned negative fd\n");
+ ALOGE("share ioctl returned negative fd");
return -EINVAL;
}
*share_fd = data.fd;
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
index d03a2b6..2754e6e 100644
--- a/liblog/logger_write.c
+++ b/liblog/logger_write.c
@@ -243,7 +243,7 @@
static int __write_to_log_daemon(log_id_t log_id, struct iovec* vec, size_t nr) {
struct android_log_transport_write* node;
- int ret;
+ int ret, save_errno;
struct timespec ts;
size_t len, i;
@@ -254,20 +254,24 @@
return -EINVAL;
}
+ save_errno = errno;
#if defined(__ANDROID__)
clock_gettime(android_log_clockid(), &ts);
if (log_id == LOG_ID_SECURITY) {
if (vec[0].iov_len < 4) {
+ errno = save_errno;
return -EINVAL;
}
ret = check_log_uid_permissions();
if (ret < 0) {
+ errno = save_errno;
return ret;
}
if (!__android_log_security()) {
/* If only we could reset downstream logd counter */
+ errno = save_errno;
return -EPERM;
}
} else if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
@@ -276,6 +280,7 @@
EventTagMap *m, *f;
if (vec[0].iov_len < 4) {
+ errno = save_errno;
return -EINVAL;
}
@@ -311,6 +316,7 @@
android_closeEventTagMap(f);
}
if (!ret) {
+ errno = save_errno;
return -EPERM;
}
} else {
@@ -340,6 +346,7 @@
}
if (!__android_log_is_loggable_len(prio, tag, len - 1, ANDROID_LOG_VERBOSE)) {
+ errno = save_errno;
return -EPERM;
}
}
@@ -371,21 +378,23 @@
}
}
+ errno = save_errno;
return ret;
}
static int __write_to_log_init(log_id_t log_id, struct iovec* vec, size_t nr) {
+ int ret, save_errno = errno;
+
__android_log_lock();
if (write_to_log == __write_to_log_init) {
- int ret;
-
ret = __write_to_log_initialize();
if (ret < 0) {
__android_log_unlock();
if (!list_empty(&__android_log_persist_write)) {
__write_to_log_daemon(log_id, vec, nr);
}
+ errno = save_errno;
return ret;
}
@@ -394,7 +403,9 @@
__android_log_unlock();
- return write_to_log(log_id, vec, nr);
+ ret = write_to_log(log_id, vec, nr);
+ errno = save_errno;
+ return ret;
}
LIBLOG_ABI_PUBLIC int __android_log_write(int prio, const char* tag,
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index b428dd7..caca377 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -20,7 +20,7 @@
},
}
-cc_library_shared {
+cc_library {
name: "libmemunreachable",
defaults: ["libmemunreachable_defaults"],
srcs: [
@@ -88,14 +88,14 @@
cc_test {
name: "memunreachable_binder_test",
defaults: ["libmemunreachable_defaults"],
+ test_suites: ["vts"],
srcs: [
"tests/Binder_test.cpp",
- "tests/MemUnreachable_test.cpp",
],
+ static_libs: ["libmemunreachable"],
shared_libs: [
"libbinder",
"libhwbinder",
- "libmemunreachable",
"libutils",
],
}
diff --git a/libmemunreachable/tests/Binder_test.cpp b/libmemunreachable/tests/Binder_test.cpp
index 6e85d5a..eaf7652 100644
--- a/libmemunreachable/tests/Binder_test.cpp
+++ b/libmemunreachable/tests/Binder_test.cpp
@@ -33,6 +33,9 @@
static const String16 service_name("test.libmemunreachable_binder");
+// Provides a service that will hold a strong reference to any remote binder
+// object, so that the test can verify that a remote strong reference is
+// visible to libmemunreachable.
class BinderService : public BBinder {
public:
BinderService() = default;
@@ -55,6 +58,8 @@
~BinderObject() = default;
};
+// Forks a subprocess that registers a BinderService with the global binder
+// servicemanager. Requires root permissions.
class ServiceProcess {
public:
ServiceProcess() : child_(0) {}
@@ -97,6 +102,7 @@
fprintf(stderr, "Failed to get service manager\n");
return 1;
}
+ // This step requires root permissions
if (sm->addService(service_name, new BinderService()) != OK) {
fprintf(stderr, "Failed to add test service\n");
return 1;
@@ -110,12 +116,18 @@
pid_t child_;
};
-class BinderTest : public ::testing::Test {
+class MemunreachableBinderTest : public ::testing::Test {
protected:
ServiceProcess service_process_;
};
-TEST_F(BinderTest, binder) {
+// Tests that a local binder object with a remote strong reference is visible
+// through the libmemunreachable BinderReferences interface, which uses the
+// getBinderKernelReferences method in libbinder. Starts a BinderService
+// through ServiceProcess as a remote service to hold the strong reference.
+TEST_F(MemunreachableBinderTest, binder) {
+ ASSERT_EQ(static_cast<uid_t>(0), getuid()) << "This test must be run as root.";
+
ServiceProcess service_process;
ASSERT_TRUE(service_process.Run());
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index 1974f2c..b0bc497 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -1,7 +1,7 @@
cc_library {
srcs: ["processgroup.cpp"],
name: "libprocessgroup",
- defaults: ["linux_bionic_supported"],
+ host_supported: true,
shared_libs: ["libbase"],
export_include_dirs: ["include"],
cflags: [
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 8526b3a..6dfa697 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -22,6 +22,7 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -33,15 +34,19 @@
#include <memory>
#include <mutex>
#include <set>
+#include <string>
#include <thread>
#include <android-base/file.h>
#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <private/android_filesystem_config.h>
#include <processgroup/processgroup.h>
+using android::base::StartsWith;
+using android::base::StringPrintf;
using android::base::WriteStringToFile;
using namespace std::chrono_literals;
@@ -50,171 +55,58 @@
#define MEM_CGROUP_TASKS "/dev/memcg/apps/tasks"
#define ACCT_CGROUP_PATH "/acct"
-#define PROCESSGROUP_UID_PREFIX "uid_"
-#define PROCESSGROUP_PID_PREFIX "pid_"
#define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
-#define PROCESSGROUP_MAX_UID_LEN 11
-#define PROCESSGROUP_MAX_PID_LEN 11
-#define PROCESSGROUP_MAX_PATH_LEN \
- ((sizeof(MEM_CGROUP_PATH) > sizeof(ACCT_CGROUP_PATH) ? \
- sizeof(MEM_CGROUP_PATH) : sizeof(ACCT_CGROUP_PATH)) + \
- sizeof(PROCESSGROUP_UID_PREFIX) + 1 + \
- PROCESSGROUP_MAX_UID_LEN + \
- sizeof(PROCESSGROUP_PID_PREFIX) + 1 + \
- PROCESSGROUP_MAX_PID_LEN + \
- sizeof(PROCESSGROUP_CGROUP_PROCS_FILE) + \
- 1)
std::once_flag init_path_flag;
-class ProcessGroup {
- public:
- ProcessGroup() : buf_ptr_(buf_), buf_len_(0) {}
-
- bool Open(uid_t uid, int pid);
-
- // Return positive number and sets *pid = next pid in process cgroup on success
- // Returns 0 if there are no pids left in the process cgroup
- // Returns -errno if an error was encountered
- int GetOneAppProcess(pid_t* pid);
-
- private:
- // Returns positive number of bytes filled on success
- // Returns 0 if there was nothing to read
- // Returns -errno if an error was encountered
- int RefillBuffer();
-
- android::base::unique_fd fd_;
- char buf_[128];
- char* buf_ptr_;
- size_t buf_len_;
-};
-
-static const char* getCgroupRootPath() {
- static const char* cgroup_root_path = NULL;
+static const std::string& GetCgroupRootPath() {
+ static std::string cgroup_root_path;
std::call_once(init_path_flag, [&]() {
// Check if mem cgroup is mounted, only then check for write-access to avoid
// SELinux denials
- cgroup_root_path = access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ?
- ACCT_CGROUP_PATH : MEM_CGROUP_PATH;
+ cgroup_root_path =
+ (access(MEM_CGROUP_TASKS, F_OK) || access(MEM_CGROUP_PATH, W_OK) ? ACCT_CGROUP_PATH
+ : MEM_CGROUP_PATH);
});
return cgroup_root_path;
}
-static int convertUidToPath(char *path, size_t size, uid_t uid)
-{
- return snprintf(path, size, "%s/%s%d",
- getCgroupRootPath(),
- PROCESSGROUP_UID_PREFIX,
- uid);
+static std::string ConvertUidToPath(uid_t uid) {
+ return StringPrintf("%s/uid_%d", GetCgroupRootPath().c_str(), uid);
}
-static int convertUidPidToPath(char *path, size_t size, uid_t uid, int pid)
-{
- return snprintf(path, size, "%s/%s%d/%s%d",
- getCgroupRootPath(),
- PROCESSGROUP_UID_PREFIX,
- uid,
- PROCESSGROUP_PID_PREFIX,
- pid);
+static std::string ConvertUidPidToPath(uid_t uid, int pid) {
+ return StringPrintf("%s/uid_%d/pid_%d", GetCgroupRootPath().c_str(), uid, pid);
}
-bool ProcessGroup::Open(uid_t uid, int pid) {
- char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
- convertUidPidToPath(path, sizeof(path), uid, pid);
- strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
-
- int fd = open(path, O_RDONLY);
- if (fd < 0) return false;
-
- fd_.reset(fd);
-
- LOG(VERBOSE) << "Initialized context for " << path;
-
- return true;
-}
-
-int ProcessGroup::RefillBuffer() {
- memmove(buf_, buf_ptr_, buf_len_);
- buf_ptr_ = buf_;
-
- ssize_t ret = read(fd_, buf_ptr_ + buf_len_, sizeof(buf_) - buf_len_ - 1);
- if (ret < 0) {
- return -errno;
- } else if (ret == 0) {
- return 0;
- }
-
- buf_len_ += ret;
- buf_[buf_len_] = 0;
- LOG(VERBOSE) << "Read " << ret << " to buffer: " << buf_;
-
- assert(buf_len_ <= sizeof(buf_));
-
- return ret;
-}
-
-int ProcessGroup::GetOneAppProcess(pid_t* out_pid) {
- *out_pid = 0;
-
- char* eptr;
- while ((eptr = static_cast<char*>(memchr(buf_ptr_, '\n', buf_len_))) == nullptr) {
- int ret = RefillBuffer();
- if (ret <= 0) return ret;
- }
-
- *eptr = '\0';
- char* pid_eptr = nullptr;
- errno = 0;
- long pid = strtol(buf_ptr_, &pid_eptr, 10);
- if (errno != 0) {
- return -errno;
- }
- if (pid_eptr != eptr) {
- errno = EINVAL;
- return -errno;
- }
-
- buf_len_ -= (eptr - buf_ptr_) + 1;
- buf_ptr_ = eptr + 1;
-
- *out_pid = static_cast<pid_t>(pid);
- return 1;
-}
-
-static int removeProcessGroup(uid_t uid, int pid)
-{
+static int RemoveProcessGroup(uid_t uid, int pid) {
int ret;
- char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
- convertUidPidToPath(path, sizeof(path), uid, pid);
- ret = rmdir(path);
+ auto uid_pid_path = ConvertUidPidToPath(uid, pid);
+ ret = rmdir(uid_pid_path.c_str());
- convertUidToPath(path, sizeof(path), uid);
- rmdir(path);
+ auto uid_path = ConvertUidToPath(uid);
+ rmdir(uid_path.c_str());
return ret;
}
-static void removeUidProcessGroups(const char *uid_path)
-{
- std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path), closedir);
+static void RemoveUidProcessGroups(const std::string& uid_path) {
+ std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path.c_str()), closedir);
if (uid != NULL) {
dirent* dir;
while ((dir = readdir(uid.get())) != nullptr) {
- char path[PROCESSGROUP_MAX_PATH_LEN];
-
if (dir->d_type != DT_DIR) {
continue;
}
- if (strncmp(dir->d_name, PROCESSGROUP_PID_PREFIX, strlen(PROCESSGROUP_PID_PREFIX))) {
+ if (!StartsWith(dir->d_name, "pid_")) {
continue;
}
- snprintf(path, sizeof(path), "%s/%s", uid_path, dir->d_name);
+ auto path = StringPrintf("%s/%s", uid_path.c_str(), dir->d_name);
LOG(VERBOSE) << "Removing " << path;
- if (rmdir(path) == -1) PLOG(WARNING) << "Failed to remove " << path;
+ if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
}
}
}
@@ -222,38 +114,38 @@
void removeAllProcessGroups()
{
LOG(VERBOSE) << "removeAllProcessGroups()";
- const char* cgroup_root_path = getCgroupRootPath();
- std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path), closedir);
+ const auto& cgroup_root_path = GetCgroupRootPath();
+ std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path.c_str()), closedir);
if (root == NULL) {
PLOG(ERROR) << "Failed to open " << cgroup_root_path;
} else {
dirent* dir;
while ((dir = readdir(root.get())) != nullptr) {
- char path[PROCESSGROUP_MAX_PATH_LEN];
-
if (dir->d_type != DT_DIR) {
continue;
}
- if (strncmp(dir->d_name, PROCESSGROUP_UID_PREFIX, strlen(PROCESSGROUP_UID_PREFIX))) {
+
+ if (!StartsWith(dir->d_name, "uid_")) {
continue;
}
- snprintf(path, sizeof(path), "%s/%s", cgroup_root_path, dir->d_name);
- removeUidProcessGroups(path);
+ auto path = StringPrintf("%s/%s", cgroup_root_path.c_str(), dir->d_name);
+ RemoveUidProcessGroups(path);
LOG(VERBOSE) << "Removing " << path;
- if (rmdir(path) == -1) PLOG(WARNING) << "Failed to remove " << path;
+ if (rmdir(path.c_str()) == -1) PLOG(WARNING) << "Failed to remove " << path;
}
}
}
// Returns number of processes killed on success
// Returns 0 if there are no processes in the process cgroup left to kill
-// Returns -errno on error
-static int doKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
- ProcessGroup process_group;
- if (!process_group.Open(uid, initialPid)) {
+// Returns -1 on error
+static int DoKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
+ auto path = ConvertUidPidToPath(uid, initialPid) + PROCESSGROUP_CGROUP_PROCS_FILE;
+ std::unique_ptr<FILE, decltype(&fclose)> fd(fopen(path.c_str(), "re"), fclose);
+ if (!fd) {
PLOG(WARNING) << "Failed to open process cgroup uid " << uid << " pid " << initialPid;
- return -errno;
+ return -1;
}
// We separate all of the pids in the cgroup into those pids that are also the leaders of
@@ -262,10 +154,9 @@
pgids.emplace(initialPid);
std::set<pid_t> pids;
- int ret;
pid_t pid;
int processes = 0;
- while ((ret = process_group.GetOneAppProcess(&pid)) > 0 && pid >= 0) {
+ while (fscanf(fd.get(), "%d\n", &pid) == 1 && pid >= 0) {
processes++;
if (pid == 0) {
// Should never happen... but if it does, trying to kill this
@@ -312,15 +203,15 @@
}
}
- return ret >= 0 ? processes : ret;
+ return feof(fd.get()) ? processes : -1;
}
-static int killProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
+static int KillProcessGroup(uid_t uid, int initialPid, int signal, int retries) {
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
int retry = retries;
int processes;
- while ((processes = doKillProcessGroupOnce(uid, initialPid, signal)) > 0) {
+ while ((processes = DoKillProcessGroupOnce(uid, initialPid, signal)) > 0) {
LOG(VERBOSE) << "Killed " << processes << " processes for processgroup " << initialPid;
if (retry > 0) {
std::this_thread::sleep_for(5ms);
@@ -350,7 +241,7 @@
LOG(INFO) << "Successfully killed process cgroup uid " << uid << " pid " << initialPid
<< " in " << static_cast<int>(ms) << "ms";
}
- return removeProcessGroup(uid, initialPid);
+ return RemoveProcessGroup(uid, initialPid);
} else {
if (retries > 0) {
LOG(ERROR) << "Failed to kill process cgroup uid " << uid << " pid " << initialPid
@@ -362,22 +253,21 @@
}
int killProcessGroup(uid_t uid, int initialPid, int signal) {
- return killProcessGroup(uid, initialPid, signal, 40 /*retries*/);
+ return KillProcessGroup(uid, initialPid, signal, 40 /*retries*/);
}
int killProcessGroupOnce(uid_t uid, int initialPid, int signal) {
- return killProcessGroup(uid, initialPid, signal, 0 /*retries*/);
+ return KillProcessGroup(uid, initialPid, signal, 0 /*retries*/);
}
-static bool mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
-{
- if (mkdir(path, mode) == -1 && errno != EEXIST) {
+static bool MkdirAndChown(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
+ if (mkdir(path.c_str(), mode) == -1 && errno != EEXIST) {
return false;
}
- if (chown(path, uid, gid) == -1) {
+ if (chown(path.c_str(), uid, gid) == -1) {
int saved_errno = errno;
- rmdir(path);
+ rmdir(path.c_str());
errno = saved_errno;
return false;
}
@@ -387,42 +277,38 @@
int createProcessGroup(uid_t uid, int initialPid)
{
- char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
+ auto uid_path = ConvertUidToPath(uid);
- convertUidToPath(path, sizeof(path), uid);
-
- if (!mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM)) {
- PLOG(ERROR) << "Failed to make and chown " << path;
+ if (!MkdirAndChown(uid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
+ PLOG(ERROR) << "Failed to make and chown " << uid_path;
return -errno;
}
- convertUidPidToPath(path, sizeof(path), uid, initialPid);
+ auto uid_pid_path = ConvertUidPidToPath(uid, initialPid);
- if (!mkdirAndChown(path, 0750, AID_SYSTEM, AID_SYSTEM)) {
- PLOG(ERROR) << "Failed to make and chown " << path;
+ if (!MkdirAndChown(uid_pid_path, 0750, AID_SYSTEM, AID_SYSTEM)) {
+ PLOG(ERROR) << "Failed to make and chown " << uid_pid_path;
return -errno;
}
- strlcat(path, PROCESSGROUP_CGROUP_PROCS_FILE, sizeof(path));
+ auto uid_pid_procs_file = uid_pid_path + PROCESSGROUP_CGROUP_PROCS_FILE;
int ret = 0;
- if (!WriteStringToFile(std::to_string(initialPid), path)) {
+ if (!WriteStringToFile(std::to_string(initialPid), uid_pid_procs_file)) {
ret = -errno;
- PLOG(ERROR) << "Failed to write '" << initialPid << "' to " << path;
+ PLOG(ERROR) << "Failed to write '" << initialPid << "' to " << uid_pid_procs_file;
}
return ret;
}
-static bool setProcessGroupValue(uid_t uid, int pid, const char* fileName, int64_t value) {
- char path[PROCESSGROUP_MAX_PATH_LEN] = {0};
- if (strcmp(getCgroupRootPath(), MEM_CGROUP_PATH)) {
- PLOG(ERROR) << "Memcg is not mounted." << path;
+static bool SetProcessGroupValue(uid_t uid, int pid, const std::string& file_name, int64_t value) {
+ if (GetCgroupRootPath() != MEM_CGROUP_PATH) {
+ PLOG(ERROR) << "Memcg is not mounted.";
return false;
}
- convertUidPidToPath(path, sizeof(path), uid, pid);
- strlcat(path, fileName, sizeof(path));
+ auto path = ConvertUidPidToPath(uid, pid) + file_name;
if (!WriteStringToFile(std::to_string(value), path)) {
PLOG(ERROR) << "Failed to write '" << value << "' to " << path;
@@ -432,13 +318,13 @@
}
bool setProcessGroupSwappiness(uid_t uid, int pid, int swappiness) {
- return setProcessGroupValue(uid, pid, "/memory.swappiness", swappiness);
+ return SetProcessGroupValue(uid, pid, "/memory.swappiness", swappiness);
}
bool setProcessGroupSoftLimit(uid_t uid, int pid, int64_t soft_limit_in_bytes) {
- return setProcessGroupValue(uid, pid, "/memory.soft_limit_in_bytes", soft_limit_in_bytes);
+ return SetProcessGroupValue(uid, pid, "/memory.soft_limit_in_bytes", soft_limit_in_bytes);
}
bool setProcessGroupLimit(uid_t uid, int pid, int64_t limit_in_bytes) {
- return setProcessGroupValue(uid, pid, "/memory.limit_in_bytes", limit_in_bytes);
+ return SetProcessGroupValue(uid, pid, "/memory.limit_in_bytes", limit_in_bytes);
}
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 7f92904..35a3063 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -137,6 +137,12 @@
switch(rta->rta_type) {
case IFLA_IFNAME:
asprintf(&mParams[0], "INTERFACE=%s", (char *) RTA_DATA(rta));
+ // We can get the interface change information from sysfs update
+ // already. But in case we missed those message when devices start.
+ // We do a update again when received a kLinkUp event. To make
+ // the message consistent, use IFINDEX here as well since sysfs
+ // uses IFINDEX.
+ asprintf(&mParams[1], "IFINDEX=%d", ifi->ifi_index);
mAction = (ifi->ifi_flags & IFF_LOWER_UP) ? Action::kLinkUp :
Action::kLinkDown;
mSubsystem = strdup("net");
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 124c70e..8717283 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -47,6 +47,8 @@
srcs: [
"ArmExidx.cpp",
+ "DexFile.cpp",
+ "DexFiles.cpp",
"DwarfCfa.cpp",
"DwarfEhFrameWithHdr.cpp",
"DwarfMemory.cpp",
@@ -85,11 +87,13 @@
},
vendor: {
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
- exclude_static_libs: ["libunwindstack_dex"],
+ exclude_srcs: [
+ "DexFile.cpp",
+ "DexFiles.cpp",
+ ],
exclude_shared_libs: ["libdexfile"],
},
},
- whole_static_libs: ["libunwindstack_dex"],
arch: {
x86: {
@@ -114,79 +118,9 @@
],
}
-// Isolate the dex file processing into a separate library. Currently,
-// it is necessary to add art include directories directly, which also
-// adds the art elf.h file in the include path, overriding the system one.
-// Work to isolate libdexfile is b/72216369.
-cc_library_static {
- name: "libunwindstack_dex",
- vendor_available: false,
- defaults: ["libunwindstack_flags"],
-
- cflags: [
- "-Wexit-time-destructors",
- ],
-
- srcs: [
- "DexFile.cpp",
- "DexFiles.cpp",
- ],
- target: {
- // Always disable optimizations for host to make it easier to debug.
- host: {
- cflags: [
- "-O0",
- "-g",
- ],
- },
- },
-
- shared_libs: [
- "libbase",
- "libdexfile",
- ],
- local_include_dirs: ["include"],
- allow_undefined_symbols: true,
-
- // libdexfile will eventually properly export headers, for now include
- // these directly.
- include_dirs: [
- "art/runtime",
- ],
-}
-
//-------------------------------------------------------------------------
// Unit Tests
//-------------------------------------------------------------------------
-cc_test_library {
- name: "libunwindstack_dex_test",
- vendor_available: false,
- defaults: ["libunwindstack_flags"],
-
- shared: {
- enabled: false,
- },
-
- srcs: [
- "tests/DexFileTest.cpp",
- "tests/DexFilesTest.cpp",
- ],
- local_include_dirs: ["include"],
- allow_undefined_symbols: true,
-
- shared_libs: [
- "libbase",
- "libunwindstack",
- "libdexfile",
- ],
-
- // libdexfile will eventually properly export headers, for now include
- // these directly.
- include_dirs: [
- "art/runtime",
- ],
-}
-
cc_test {
name: "libunwindstack_test",
defaults: ["libunwindstack_flags"],
@@ -194,6 +128,8 @@
srcs: [
"tests/ArmExidxDecodeTest.cpp",
"tests/ArmExidxExtractTest.cpp",
+ "tests/DexFileTest.cpp",
+ "tests/DexFilesTest.cpp",
"tests/DwarfCfaLogTest.cpp",
"tests/DwarfCfaTest.cpp",
"tests/DwarfDebugFrameTest.cpp",
@@ -242,14 +178,13 @@
"liblog",
"liblzma",
"libunwindstack",
+ "libdexfile",
],
static_libs: [
"libgmock",
],
- whole_static_libs: ["libunwindstack_dex_test"],
-
data: [
"tests/files/elf32.xz",
"tests/files/elf64.xz",
diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp
index c5f8138..430f6c5 100644
--- a/libunwindstack/DexFiles.cpp
+++ b/libunwindstack/DexFiles.cpp
@@ -77,7 +77,8 @@
uint64_t DexFiles::ReadEntryPtr32(uint64_t addr) {
uint32_t entry;
- if (!memory_->ReadFully(addr, &entry, sizeof(entry))) {
+ const uint32_t field_offset = 12; // offset of first_entry_ in the descriptor struct.
+ if (!memory_->ReadFully(addr + field_offset, &entry, sizeof(entry))) {
return 0;
}
return entry;
@@ -85,7 +86,8 @@
uint64_t DexFiles::ReadEntryPtr64(uint64_t addr) {
uint64_t entry;
- if (!memory_->ReadFully(addr, &entry, sizeof(entry))) {
+ const uint32_t field_offset = 16; // offset of first_entry_ in the descriptor struct.
+ if (!memory_->ReadFully(addr + field_offset, &entry, sizeof(entry))) {
return 0;
}
return entry;
@@ -122,7 +124,7 @@
initialized_ = true;
entry_addr_ = 0;
- const std::string dex_debug_name("__art_debug_dexfiles");
+ const std::string dex_debug_name("__dex_debug_descriptor");
for (MapInfo* info : *maps) {
if (!(info->flags & PROT_EXEC) || !(info->flags & PROT_READ) || info->offset != 0) {
continue;
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index 4fc95c7..aa8cd3a 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -424,7 +424,10 @@
template <typename AddressType>
bool DwarfCfa<AddressType>::cfa_def_cfa_expression(dwarf_loc_regs_t* loc_regs) {
- (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_EXPRESSION,
+ // There is only one type of expression for CFA evaluation and the DWARF
+ // specification is unclear whether it returns the address or the
+ // dereferenced value. GDB expects the value, so will we.
+ (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_VAL_EXPRESSION,
.values = {operands_[0], memory_->cur_offset()}};
return true;
}
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 7649798..ddbc12e 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -214,21 +214,13 @@
}
eval_info.cfa += loc->values[1];
break;
- case DWARF_LOCATION_EXPRESSION:
case DWARF_LOCATION_VAL_EXPRESSION: {
AddressType value;
if (!EvalExpression(*loc, regular_memory, &value, &eval_info.regs_info, nullptr)) {
return false;
}
- if (loc->type == DWARF_LOCATION_EXPRESSION) {
- if (!regular_memory->ReadFully(value, &eval_info.cfa, sizeof(AddressType))) {
- last_error_.code = DWARF_ERROR_MEMORY_INVALID;
- last_error_.address = value;
- return false;
- }
- } else {
- eval_info.cfa = value;
- }
+ // There is only one type of valid expression for CFA evaluation.
+ eval_info.cfa = value;
break;
}
default:
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index dfb8e8f..a5afc7e 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -171,4 +171,17 @@
return return_value;
}
+bool ElfInterfaceArm::GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
+ uint64_t* offset) {
+ // For ARM, thumb function symbols have bit 0 set, but the address passed
+ // in here might not have this bit set and result in a failure to find
+ // the thumb function names. Adjust the address and offset to account
+ // for this possible case.
+ if (ElfInterface32::GetFunctionName(addr | 1, load_bias, name, offset)) {
+ *offset &= ~1;
+ return true;
+ }
+ return false;
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index 9c067ba..c1597ce 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -76,6 +76,9 @@
bool StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
bool* finished);
+ bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
+ uint64_t* offset) override;
+
uint64_t start_offset() { return start_offset_; }
size_t total_entries() { return total_entries_; }
diff --git a/libunwindstack/RegsArm.cpp b/libunwindstack/RegsArm.cpp
index 7f16146..5502ce1 100644
--- a/libunwindstack/RegsArm.cpp
+++ b/libunwindstack/RegsArm.cpp
@@ -35,26 +35,25 @@
return ARCH_ARM;
}
-uint64_t RegsArm::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
+uint64_t RegsArm::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
uint64_t load_bias = elf->GetLoadBias();
if (rel_pc < load_bias) {
- return rel_pc;
+ return 0;
}
uint64_t adjusted_rel_pc = rel_pc - load_bias;
-
if (adjusted_rel_pc < 5) {
- return rel_pc;
+ return 0;
}
if (adjusted_rel_pc & 1) {
// This is a thumb instruction, it could be 2 or 4 bytes.
uint32_t value;
- if (rel_pc < 5 || !elf->memory()->ReadFully(adjusted_rel_pc - 5, &value, sizeof(value)) ||
+ if (!elf->memory()->ReadFully(adjusted_rel_pc - 5, &value, sizeof(value)) ||
(value & 0xe000f000) != 0xe000f000) {
- return rel_pc - 2;
+ return 2;
}
}
- return rel_pc - 4;
+ return 4;
}
void RegsArm::SetFromRaw() {
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
index d6b467a..cc6f5ce 100644
--- a/libunwindstack/RegsArm64.cpp
+++ b/libunwindstack/RegsArm64.cpp
@@ -35,15 +35,11 @@
return ARCH_ARM64;
}
-uint64_t RegsArm64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
- if (!elf->valid()) {
- return rel_pc;
+uint64_t RegsArm64::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
+ if (!elf->valid() || rel_pc < 4) {
+ return 0;
}
-
- if (rel_pc < 4) {
- return rel_pc;
- }
- return rel_pc - 4;
+ return 4;
}
void RegsArm64::SetFromRaw() {
diff --git a/libunwindstack/RegsMips.cpp b/libunwindstack/RegsMips.cpp
index 6751f52..5d20bef 100644
--- a/libunwindstack/RegsMips.cpp
+++ b/libunwindstack/RegsMips.cpp
@@ -35,16 +35,12 @@
return ARCH_MIPS;
}
-uint64_t RegsMips::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
- if (!elf->valid()) {
- return rel_pc;
+uint64_t RegsMips::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
+ if (!elf->valid() || rel_pc < 8) {
+ return 0;
}
-
- // For now, just assuming no compact branches
- if (rel_pc < 8) {
- return rel_pc;
- }
- return rel_pc - 8;
+ // For now, just assume no compact branches
+ return 8;
}
void RegsMips::SetFromRaw() {
diff --git a/libunwindstack/RegsMips64.cpp b/libunwindstack/RegsMips64.cpp
index 97082bd..4a03538 100644
--- a/libunwindstack/RegsMips64.cpp
+++ b/libunwindstack/RegsMips64.cpp
@@ -36,16 +36,12 @@
return ARCH_MIPS64;
}
-uint64_t RegsMips64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
- if (!elf->valid()) {
- return rel_pc;
+uint64_t RegsMips64::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
+ if (!elf->valid() || rel_pc < 8) {
+ return 0;
}
-
- // For now, just assuming no compact branches
- if (rel_pc < 8) {
- return rel_pc;
- }
- return rel_pc - 8;
+ // For now, just assume no compact branches
+ return 8;
}
void RegsMips64::SetFromRaw() {
diff --git a/libunwindstack/RegsX86.cpp b/libunwindstack/RegsX86.cpp
index 27476b7..573cb23 100644
--- a/libunwindstack/RegsX86.cpp
+++ b/libunwindstack/RegsX86.cpp
@@ -35,15 +35,11 @@
return ARCH_X86;
}
-uint64_t RegsX86::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
- if (!elf->valid()) {
- return rel_pc;
- }
-
- if (rel_pc == 0) {
+uint64_t RegsX86::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
+ if (!elf->valid() || rel_pc == 0) {
return 0;
}
- return rel_pc - 1;
+ return 1;
}
void RegsX86::SetFromRaw() {
diff --git a/libunwindstack/RegsX86_64.cpp b/libunwindstack/RegsX86_64.cpp
index 0f66943..3175a90 100644
--- a/libunwindstack/RegsX86_64.cpp
+++ b/libunwindstack/RegsX86_64.cpp
@@ -35,16 +35,11 @@
return ARCH_X86_64;
}
-uint64_t RegsX86_64::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
- if (!elf->valid()) {
- return rel_pc;
- }
-
- if (rel_pc == 0) {
+uint64_t RegsX86_64::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
+ if (!elf->valid() || rel_pc == 0) {
return 0;
}
-
- return rel_pc - 1;
+ return 1;
}
void RegsX86_64::SetFromRaw() {
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 5a4f5e0..94edb1c 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -77,26 +77,25 @@
return;
}
- // dex_files_->GetMethodInformation(dex_pc - dex_offset, dex_offset, info, &frame->function_name,
dex_files_->GetMethodInformation(maps_, info, dex_pc, &frame->function_name,
&frame->function_offset);
#endif
}
-void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc, uint64_t func_pc) {
+void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t func_pc,
+ uint64_t pc_adjustment) {
size_t frame_num = frames_.size();
frames_.resize(frame_num + 1);
FrameData* frame = &frames_.at(frame_num);
frame->num = frame_num;
frame->sp = regs_->sp();
- frame->rel_pc = adjusted_rel_pc;
+ frame->rel_pc = rel_pc - pc_adjustment;
+ frame->pc = regs_->pc() - pc_adjustment;
if (map_info == nullptr) {
- frame->pc = regs_->pc();
return;
}
- frame->pc = map_info->start + adjusted_rel_pc - elf->GetLoadBias() - map_info->elf_offset;
frame->map_name = map_info->name;
frame->map_offset = map_info->offset;
frame->map_start = map_info->start;
@@ -140,13 +139,12 @@
MapInfo* map_info = maps_->Find(regs_->pc());
uint64_t rel_pc;
- uint64_t adjusted_pc;
- uint64_t adjusted_rel_pc;
+ uint64_t pc_adjustment = 0;
+ uint64_t step_pc;
Elf* elf;
if (map_info == nullptr) {
rel_pc = regs_->pc();
- adjusted_rel_pc = rel_pc;
- adjusted_pc = rel_pc;
+ step_pc = rel_pc;
last_error_.code = ERROR_INVALID_MAP;
} else {
if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
@@ -155,21 +153,20 @@
elf = map_info->GetElf(process_memory_, true);
rel_pc = elf->GetRelPc(regs_->pc(), map_info);
if (adjust_pc) {
- adjusted_pc = regs_->GetAdjustedPc(rel_pc, elf);
+ pc_adjustment = regs_->GetPcAdjustment(rel_pc, elf);
} else {
- adjusted_pc = rel_pc;
+ pc_adjustment = 0;
}
- adjusted_rel_pc = adjusted_pc;
+ step_pc = rel_pc - pc_adjustment;
// If the pc is in an invalid elf file, try and get an Elf object
// using the jit debug information.
if (!elf->valid() && jit_debug_ != nullptr) {
- uint64_t adjusted_jit_pc = regs_->pc() - (rel_pc - adjusted_pc);
+ uint64_t adjusted_jit_pc = regs_->pc() - pc_adjustment;
Elf* jit_elf = jit_debug_->GetElf(maps_, adjusted_jit_pc);
if (jit_elf != nullptr) {
// The jit debug information requires a non relative adjusted pc.
- adjusted_pc = adjusted_jit_pc;
- adjusted_rel_pc = adjusted_pc - map_info->start;
+ step_pc = adjusted_jit_pc;
elf = jit_elf;
}
}
@@ -185,7 +182,7 @@
regs_->set_dex_pc(0);
}
- FillInFrame(map_info, elf, adjusted_rel_pc, adjusted_pc);
+ FillInFrame(map_info, elf, rel_pc, step_pc, pc_adjustment);
// Once a frame is added, stop skipping frames.
initial_map_names_to_skip = nullptr;
@@ -213,8 +210,8 @@
in_device_map = true;
} else {
bool finished;
- stepped = elf->Step(rel_pc, adjusted_pc, map_info->elf_offset, regs_,
- process_memory_.get(), &finished);
+ stepped = elf->Step(rel_pc, step_pc, map_info->elf_offset, regs_, process_memory_.get(),
+ &finished);
elf->GetLastError(&last_error_);
if (stepped && finished) {
break;
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index a5ba7a0..b0e7ea1 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -60,7 +60,7 @@
uint64_t dex_pc() { return dex_pc_; }
void set_dex_pc(uint64_t dex_pc) { dex_pc_ = dex_pc; }
- virtual uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) = 0;
+ virtual uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) = 0;
virtual bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) = 0;
diff --git a/libunwindstack/include/unwindstack/RegsArm.h b/libunwindstack/include/unwindstack/RegsArm.h
index b5d344b..5af90d3 100644
--- a/libunwindstack/include/unwindstack/RegsArm.h
+++ b/libunwindstack/include/unwindstack/RegsArm.h
@@ -36,7 +36,7 @@
virtual ArchEnum Arch() override final;
- uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+ uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
void SetFromRaw() override;
diff --git a/libunwindstack/include/unwindstack/RegsArm64.h b/libunwindstack/include/unwindstack/RegsArm64.h
index 30e626c..cb05732 100644
--- a/libunwindstack/include/unwindstack/RegsArm64.h
+++ b/libunwindstack/include/unwindstack/RegsArm64.h
@@ -36,7 +36,7 @@
virtual ArchEnum Arch() override final;
- uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+ uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
void SetFromRaw() override;
diff --git a/libunwindstack/include/unwindstack/RegsMips.h b/libunwindstack/include/unwindstack/RegsMips.h
index 3fe6a9f..8e3c01f 100644
--- a/libunwindstack/include/unwindstack/RegsMips.h
+++ b/libunwindstack/include/unwindstack/RegsMips.h
@@ -36,7 +36,7 @@
virtual ArchEnum Arch() override final;
- uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+ uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
void SetFromRaw() override;
diff --git a/libunwindstack/include/unwindstack/RegsMips64.h b/libunwindstack/include/unwindstack/RegsMips64.h
index 6b4bcdf..8c2d443 100644
--- a/libunwindstack/include/unwindstack/RegsMips64.h
+++ b/libunwindstack/include/unwindstack/RegsMips64.h
@@ -36,7 +36,7 @@
virtual ArchEnum Arch() override final;
- uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+ uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
void SetFromRaw() override;
diff --git a/libunwindstack/include/unwindstack/RegsX86.h b/libunwindstack/include/unwindstack/RegsX86.h
index a695bbf..1bc145d 100644
--- a/libunwindstack/include/unwindstack/RegsX86.h
+++ b/libunwindstack/include/unwindstack/RegsX86.h
@@ -37,7 +37,7 @@
virtual ArchEnum Arch() override final;
- uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+ uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
void SetFromRaw() override;
diff --git a/libunwindstack/include/unwindstack/RegsX86_64.h b/libunwindstack/include/unwindstack/RegsX86_64.h
index 23a3f20..4cd45d4 100644
--- a/libunwindstack/include/unwindstack/RegsX86_64.h
+++ b/libunwindstack/include/unwindstack/RegsX86_64.h
@@ -37,7 +37,7 @@
virtual ArchEnum Arch() override final;
- uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+ uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
void SetFromRaw() override;
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 5770c1a..56b0581 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -88,7 +88,8 @@
private:
void FillInDexFrame();
- void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc, uint64_t adjusted_pc);
+ void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, uint64_t func_pc,
+ uint64_t pc_adjustment);
size_t max_frames_;
Maps* maps_;
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
index dca5605..d029bb0 100644
--- a/libunwindstack/tests/DexFilesTest.cpp
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -63,7 +63,7 @@
elf->FakeSetValid(true);
ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
elf->FakeSetInterface(interface);
- interface->FakeSetGlobalVariable("__art_debug_dexfiles", 0x800);
+ interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
map_info->elf.reset(elf);
// Global variable not set by default.
@@ -74,7 +74,7 @@
elf->FakeSetValid(true);
interface = new ElfInterfaceFake(memory);
elf->FakeSetInterface(interface);
- interface->FakeSetGlobalVariable("__art_debug_dexfiles", 0x800);
+ interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
map_info->elf.reset(elf);
// Global variable set in this map.
@@ -85,10 +85,12 @@
elf->FakeSetValid(true);
interface = new ElfInterfaceFake(memory);
elf->FakeSetInterface(interface);
- interface->FakeSetGlobalVariable("__art_debug_dexfiles", 0x800);
+ interface->FakeSetGlobalVariable("__dex_debug_descriptor", 0x800);
map_info->elf.reset(elf);
}
+ void WriteDescriptor32(uint64_t addr, uint32_t head);
+ void WriteDescriptor64(uint64_t addr, uint64_t head);
void WriteEntry32(uint64_t entry_addr, uint32_t next, uint32_t prev, uint32_t dex_file);
void WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev, uint64_t dex_file);
void WriteDex(uint64_t dex_file);
@@ -105,6 +107,16 @@
std::unique_ptr<BufferMaps> maps_;
};
+void DexFilesTest::WriteDescriptor32(uint64_t addr, uint32_t head) {
+ // void* first_entry_
+ memory_->SetData32(addr + 12, head);
+}
+
+void DexFilesTest::WriteDescriptor64(uint64_t addr, uint64_t head) {
+ // void* first_entry_
+ memory_->SetData64(addr + 16, head);
+}
+
void DexFilesTest::WriteEntry32(uint64_t entry_addr, uint32_t next, uint32_t prev,
uint32_t dex_file) {
// Format of the 32 bit DEXFileEntry structure:
@@ -146,7 +158,7 @@
uint64_t method_offset = 0x124;
MapInfo* info = maps_->Get(kMapDexFiles);
- memory_->SetData32(0xf800, 0x200000);
+ WriteDescriptor32(0xf800, 0x200000);
WriteEntry32(0x200000, 0, 0, 0x300000);
WriteDex(0x300000);
@@ -161,7 +173,7 @@
MapInfo* info = maps_->Get(kMapDexFiles);
dex_files_->SetArch(ARCH_ARM64);
- memory_->SetData64(0xf800, 0x200000);
+ WriteDescriptor64(0xf800, 0x200000);
WriteEntry64(0x200000, 0, 0, 0x301000);
WriteDex(0x301000);
@@ -175,7 +187,7 @@
uint64_t method_offset = 0x124;
MapInfo* info = maps_->Get(kMapDexFiles);
- memory_->SetData32(0xf800, 0x200000);
+ WriteDescriptor32(0xf800, 0x200000);
WriteEntry32(0x200000, 0x200100, 0, 0x100000);
WriteEntry32(0x200100, 0, 0x200000, 0x300000);
WriteDex(0x300000);
@@ -191,7 +203,7 @@
MapInfo* info = maps_->Get(kMapDexFiles);
dex_files_->SetArch(ARCH_ARM64);
- memory_->SetData64(0xf800, 0x200000);
+ WriteDescriptor64(0xf800, 0x200000);
WriteEntry64(0x200000, 0x200100, 0, 0x100000);
WriteEntry64(0x200100, 0, 0x200000, 0x300000);
WriteDex(0x300000);
@@ -206,7 +218,7 @@
uint64_t method_offset = 0x124;
MapInfo* info = maps_->Get(kMapDexFiles);
- memory_->SetData32(0xf800, 0x200000);
+ WriteDescriptor32(0xf800, 0x200000);
WriteEntry32(0x200000, 0, 0, 0x300000);
WriteDex(0x300000);
@@ -226,7 +238,7 @@
uint64_t method_offset = 0x124;
MapInfo* info = maps_->Get(kMapDexFiles);
- memory_->SetData32(0xf800, 0x200000);
+ WriteDescriptor32(0xf800, 0x200000);
WriteEntry32(0x200000, 0x200100, 0, 0x100000);
WriteEntry32(0x200100, 0, 0x200000, 0x300000);
WriteDex(0x300000);
@@ -259,9 +271,9 @@
MapInfo* info = maps_->Get(kMapDexFiles);
// First global variable found, but value is zero.
- memory_->SetData32(0xc800, 0);
+ WriteDescriptor32(0xc800, 0);
- memory_->SetData32(0xf800, 0x200000);
+ WriteDescriptor32(0xf800, 0x200000);
WriteEntry32(0x200000, 0, 0, 0x300000);
WriteDex(0x300000);
@@ -274,7 +286,7 @@
dex_files_->SetArch(ARCH_ARM);
method_name = "fail";
method_offset = 0x123;
- memory_->SetData32(0xc800, 0x100000);
+ WriteDescriptor32(0xc800, 0x100000);
dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
EXPECT_EQ("fail", method_name);
EXPECT_EQ(0x123U, method_offset);
@@ -286,9 +298,9 @@
MapInfo* info = maps_->Get(kMapDexFiles);
// First global variable found, but value is zero.
- memory_->SetData64(0xc800, 0);
+ WriteDescriptor64(0xc800, 0);
- memory_->SetData64(0xf800, 0x200000);
+ WriteDescriptor64(0xf800, 0x200000);
WriteEntry64(0x200000, 0, 0, 0x300000);
WriteDex(0x300000);
@@ -302,7 +314,7 @@
dex_files_->SetArch(ARCH_ARM64);
method_name = "fail";
method_offset = 0x123;
- memory_->SetData32(0xc800, 0x100000);
+ WriteDescriptor64(0xc800, 0x100000);
dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
EXPECT_EQ("fail", method_name);
EXPECT_EQ(0x123U, method_offset);
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
index 68dc30c..7395b04 100644
--- a/libunwindstack/tests/DwarfCfaTest.cpp
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -737,6 +737,8 @@
ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x284, &loc_regs));
ASSERT_EQ(0x284U, this->dmem_->cur_offset());
ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_VAL_EXPRESSION, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x81U, loc_regs[CFA_REG].values[0]);
ASSERT_EQ("", GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index 37305b2..99f4d87 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -99,7 +99,7 @@
regs.set_sp(0x2000);
regs[5] = 0x20;
regs[9] = 0x3000;
- loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5002}};
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x2, 0x5002}};
bool finished;
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
@@ -116,7 +116,7 @@
regs[5] = 0x20;
regs[9] = 0x3000;
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x96, 0x96, 0x96});
- loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5002}};
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x2, 0x5002}};
bool finished;
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->LastErrorCode());
@@ -136,10 +136,8 @@
this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5004}};
bool finished;
- ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
- EXPECT_FALSE(finished);
- EXPECT_EQ(0x12345U, regs.sp());
- EXPECT_EQ(0x20U, regs.pc());
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
}
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_val_expr) {
@@ -170,7 +168,7 @@
regs[5] = 0x20;
regs[9] = 0x3000;
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x50, 0x96, 0x96});
- loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5002}};
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x2, 0x5002}};
bool finished;
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->section_->LastErrorCode());
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index cd7f2ff..ab23194 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -47,7 +47,7 @@
bool Is32Bit() { return false; }
- uint64_t GetAdjustedPc(uint64_t rel_pc, Elf*) override { return rel_pc - 2; }
+ uint64_t GetPcAdjustment(uint64_t, Elf*) override { return 2; }
bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
@@ -77,7 +77,7 @@
ArchEnum Arch() override { return ARCH_UNKNOWN; }
- uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
+ uint64_t GetPcAdjustment(uint64_t, Elf*) override { return 0; }
void SetFromRaw() override {}
bool SetPcFromReturnAddress(Memory*) override { return false; }
bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 7c06373..8b2f6c8 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -96,48 +96,48 @@
TEST_F(RegsTest, rel_pc) {
RegsArm64 arm64;
- ASSERT_EQ(0xcU, arm64.GetAdjustedPc(0x10, elf_.get()));
- ASSERT_EQ(0x0U, arm64.GetAdjustedPc(0x4, elf_.get()));
- ASSERT_EQ(0x3U, arm64.GetAdjustedPc(0x3, elf_.get()));
- ASSERT_EQ(0x2U, arm64.GetAdjustedPc(0x2, elf_.get()));
- ASSERT_EQ(0x1U, arm64.GetAdjustedPc(0x1, elf_.get()));
- ASSERT_EQ(0x0U, arm64.GetAdjustedPc(0x0, elf_.get()));
+ ASSERT_EQ(4U, arm64.GetPcAdjustment(0x10, elf_.get()));
+ ASSERT_EQ(4U, arm64.GetPcAdjustment(0x4, elf_.get()));
+ ASSERT_EQ(0U, arm64.GetPcAdjustment(0x3, elf_.get()));
+ ASSERT_EQ(0U, arm64.GetPcAdjustment(0x2, elf_.get()));
+ ASSERT_EQ(0U, arm64.GetPcAdjustment(0x1, elf_.get()));
+ ASSERT_EQ(0U, arm64.GetPcAdjustment(0x0, elf_.get()));
RegsX86 x86;
- ASSERT_EQ(0xffU, x86.GetAdjustedPc(0x100, elf_.get()));
- ASSERT_EQ(0x1U, x86.GetAdjustedPc(0x2, elf_.get()));
- ASSERT_EQ(0x0U, x86.GetAdjustedPc(0x1, elf_.get()));
- ASSERT_EQ(0x0U, x86.GetAdjustedPc(0x0, elf_.get()));
+ ASSERT_EQ(1U, x86.GetPcAdjustment(0x100, elf_.get()));
+ ASSERT_EQ(1U, x86.GetPcAdjustment(0x2, elf_.get()));
+ ASSERT_EQ(1U, x86.GetPcAdjustment(0x1, elf_.get()));
+ ASSERT_EQ(0U, x86.GetPcAdjustment(0x0, elf_.get()));
RegsX86_64 x86_64;
- ASSERT_EQ(0xffU, x86_64.GetAdjustedPc(0x100, elf_.get()));
- ASSERT_EQ(0x1U, x86_64.GetAdjustedPc(0x2, elf_.get()));
- ASSERT_EQ(0x0U, x86_64.GetAdjustedPc(0x1, elf_.get()));
- ASSERT_EQ(0x0U, x86_64.GetAdjustedPc(0x0, elf_.get()));
+ ASSERT_EQ(1U, x86_64.GetPcAdjustment(0x100, elf_.get()));
+ ASSERT_EQ(1U, x86_64.GetPcAdjustment(0x2, elf_.get()));
+ ASSERT_EQ(1U, x86_64.GetPcAdjustment(0x1, elf_.get()));
+ ASSERT_EQ(0U, x86_64.GetPcAdjustment(0x0, elf_.get()));
RegsMips mips;
- ASSERT_EQ(0x8U, mips.GetAdjustedPc(0x10, elf_.get()));
- ASSERT_EQ(0x0U, mips.GetAdjustedPc(0x8, elf_.get()));
- ASSERT_EQ(0x7U, mips.GetAdjustedPc(0x7, elf_.get()));
- ASSERT_EQ(0x6U, mips.GetAdjustedPc(0x6, elf_.get()));
- ASSERT_EQ(0x5U, mips.GetAdjustedPc(0x5, elf_.get()));
- ASSERT_EQ(0x4U, mips.GetAdjustedPc(0x4, elf_.get()));
- ASSERT_EQ(0x3U, mips.GetAdjustedPc(0x3, elf_.get()));
- ASSERT_EQ(0x2U, mips.GetAdjustedPc(0x2, elf_.get()));
- ASSERT_EQ(0x1U, mips.GetAdjustedPc(0x1, elf_.get()));
- ASSERT_EQ(0x0U, mips.GetAdjustedPc(0x0, elf_.get()));
+ ASSERT_EQ(8U, mips.GetPcAdjustment(0x10, elf_.get()));
+ ASSERT_EQ(8U, mips.GetPcAdjustment(0x8, elf_.get()));
+ ASSERT_EQ(0U, mips.GetPcAdjustment(0x7, elf_.get()));
+ ASSERT_EQ(0U, mips.GetPcAdjustment(0x6, elf_.get()));
+ ASSERT_EQ(0U, mips.GetPcAdjustment(0x5, elf_.get()));
+ ASSERT_EQ(0U, mips.GetPcAdjustment(0x4, elf_.get()));
+ ASSERT_EQ(0U, mips.GetPcAdjustment(0x3, elf_.get()));
+ ASSERT_EQ(0U, mips.GetPcAdjustment(0x2, elf_.get()));
+ ASSERT_EQ(0U, mips.GetPcAdjustment(0x1, elf_.get()));
+ ASSERT_EQ(0U, mips.GetPcAdjustment(0x0, elf_.get()));
RegsMips64 mips64;
- ASSERT_EQ(0x8U, mips64.GetAdjustedPc(0x10, elf_.get()));
- ASSERT_EQ(0x0U, mips64.GetAdjustedPc(0x8, elf_.get()));
- ASSERT_EQ(0x7U, mips64.GetAdjustedPc(0x7, elf_.get()));
- ASSERT_EQ(0x6U, mips64.GetAdjustedPc(0x6, elf_.get()));
- ASSERT_EQ(0x5U, mips64.GetAdjustedPc(0x5, elf_.get()));
- ASSERT_EQ(0x4U, mips64.GetAdjustedPc(0x4, elf_.get()));
- ASSERT_EQ(0x3U, mips64.GetAdjustedPc(0x3, elf_.get()));
- ASSERT_EQ(0x2U, mips64.GetAdjustedPc(0x2, elf_.get()));
- ASSERT_EQ(0x1U, mips64.GetAdjustedPc(0x1, elf_.get()));
- ASSERT_EQ(0x0U, mips64.GetAdjustedPc(0x0, elf_.get()));
+ ASSERT_EQ(8U, mips64.GetPcAdjustment(0x10, elf_.get()));
+ ASSERT_EQ(8U, mips64.GetPcAdjustment(0x8, elf_.get()));
+ ASSERT_EQ(0U, mips64.GetPcAdjustment(0x7, elf_.get()));
+ ASSERT_EQ(0U, mips64.GetPcAdjustment(0x6, elf_.get()));
+ ASSERT_EQ(0U, mips64.GetPcAdjustment(0x5, elf_.get()));
+ ASSERT_EQ(0U, mips64.GetPcAdjustment(0x4, elf_.get()));
+ ASSERT_EQ(0U, mips64.GetPcAdjustment(0x3, elf_.get()));
+ ASSERT_EQ(0U, mips64.GetPcAdjustment(0x2, elf_.get()));
+ ASSERT_EQ(0U, mips64.GetPcAdjustment(0x1, elf_.get()));
+ ASSERT_EQ(0U, mips64.GetPcAdjustment(0x0, elf_.get()));
}
TEST_F(RegsTest, rel_pc_arm) {
@@ -145,34 +145,34 @@
// Check fence posts.
elf_->FakeSetLoadBias(0);
- ASSERT_EQ(3U, arm.GetAdjustedPc(0x5, elf_.get()));
- ASSERT_EQ(4U, arm.GetAdjustedPc(0x4, elf_.get()));
- ASSERT_EQ(3U, arm.GetAdjustedPc(0x3, elf_.get()));
- ASSERT_EQ(2U, arm.GetAdjustedPc(0x2, elf_.get()));
- ASSERT_EQ(1U, arm.GetAdjustedPc(0x1, elf_.get()));
- ASSERT_EQ(0U, arm.GetAdjustedPc(0x0, elf_.get()));
+ ASSERT_EQ(2U, arm.GetPcAdjustment(0x5, elf_.get()));
+ ASSERT_EQ(0U, arm.GetPcAdjustment(0x4, elf_.get()));
+ ASSERT_EQ(0U, arm.GetPcAdjustment(0x3, elf_.get()));
+ ASSERT_EQ(0U, arm.GetPcAdjustment(0x2, elf_.get()));
+ ASSERT_EQ(0U, arm.GetPcAdjustment(0x1, elf_.get()));
+ ASSERT_EQ(0U, arm.GetPcAdjustment(0x0, elf_.get()));
elf_->FakeSetLoadBias(0x100);
- ASSERT_EQ(0xffU, arm.GetAdjustedPc(0xff, elf_.get()));
- ASSERT_EQ(0x103U, arm.GetAdjustedPc(0x105, elf_.get()));
- ASSERT_EQ(0x104U, arm.GetAdjustedPc(0x104, elf_.get()));
- ASSERT_EQ(0x103U, arm.GetAdjustedPc(0x103, elf_.get()));
- ASSERT_EQ(0x102U, arm.GetAdjustedPc(0x102, elf_.get()));
- ASSERT_EQ(0x101U, arm.GetAdjustedPc(0x101, elf_.get()));
- ASSERT_EQ(0x100U, arm.GetAdjustedPc(0x100, elf_.get()));
+ ASSERT_EQ(0U, arm.GetPcAdjustment(0xff, elf_.get()));
+ ASSERT_EQ(2U, arm.GetPcAdjustment(0x105, elf_.get()));
+ ASSERT_EQ(0U, arm.GetPcAdjustment(0x104, elf_.get()));
+ ASSERT_EQ(0U, arm.GetPcAdjustment(0x103, elf_.get()));
+ ASSERT_EQ(0U, arm.GetPcAdjustment(0x102, elf_.get()));
+ ASSERT_EQ(0U, arm.GetPcAdjustment(0x101, elf_.get()));
+ ASSERT_EQ(0U, arm.GetPcAdjustment(0x100, elf_.get()));
// Check thumb instructions handling.
elf_->FakeSetLoadBias(0);
memory_->SetData32(0x2000, 0);
- ASSERT_EQ(0x2003U, arm.GetAdjustedPc(0x2005, elf_.get()));
+ ASSERT_EQ(2U, arm.GetPcAdjustment(0x2005, elf_.get()));
memory_->SetData32(0x2000, 0xe000f000);
- ASSERT_EQ(0x2001U, arm.GetAdjustedPc(0x2005, elf_.get()));
+ ASSERT_EQ(4U, arm.GetPcAdjustment(0x2005, elf_.get()));
elf_->FakeSetLoadBias(0x400);
memory_->SetData32(0x2100, 0);
- ASSERT_EQ(0x2503U, arm.GetAdjustedPc(0x2505, elf_.get()));
+ ASSERT_EQ(2U, arm.GetPcAdjustment(0x2505, elf_.get()));
memory_->SetData32(0x2100, 0xf111f111);
- ASSERT_EQ(0x2501U, arm.GetAdjustedPc(0x2505, elf_.get()));
+ ASSERT_EQ(4U, arm.GetPcAdjustment(0x2505, elf_.get()));
}
TEST_F(RegsTest, elf_invalid) {
@@ -188,27 +188,27 @@
regs_arm.set_pc(0x1500);
EXPECT_EQ(0x500U, invalid_elf->GetRelPc(regs_arm.pc(), &map_info));
- EXPECT_EQ(0x4fcU, regs_arm.GetAdjustedPc(0x500U, invalid_elf));
+ EXPECT_EQ(4U, regs_arm.GetPcAdjustment(0x500U, invalid_elf));
regs_arm64.set_pc(0x1600);
EXPECT_EQ(0x600U, invalid_elf->GetRelPc(regs_arm64.pc(), &map_info));
- EXPECT_EQ(0x600U, regs_arm64.GetAdjustedPc(0x600U, invalid_elf));
+ EXPECT_EQ(0U, regs_arm64.GetPcAdjustment(0x600U, invalid_elf));
regs_x86.set_pc(0x1700);
EXPECT_EQ(0x700U, invalid_elf->GetRelPc(regs_x86.pc(), &map_info));
- EXPECT_EQ(0x700U, regs_x86.GetAdjustedPc(0x700U, invalid_elf));
+ EXPECT_EQ(0U, regs_x86.GetPcAdjustment(0x700U, invalid_elf));
regs_x86_64.set_pc(0x1800);
EXPECT_EQ(0x800U, invalid_elf->GetRelPc(regs_x86_64.pc(), &map_info));
- EXPECT_EQ(0x800U, regs_x86_64.GetAdjustedPc(0x800U, invalid_elf));
+ EXPECT_EQ(0U, regs_x86_64.GetPcAdjustment(0x800U, invalid_elf));
regs_mips.set_pc(0x1900);
EXPECT_EQ(0x900U, invalid_elf->GetRelPc(regs_mips.pc(), &map_info));
- EXPECT_EQ(0x900U, regs_mips.GetAdjustedPc(0x900U, invalid_elf));
+ EXPECT_EQ(0U, regs_mips.GetPcAdjustment(0x900U, invalid_elf));
regs_mips64.set_pc(0x1a00);
EXPECT_EQ(0xa00U, invalid_elf->GetRelPc(regs_mips64.pc(), &map_info));
- EXPECT_EQ(0xa00U, regs_mips64.GetAdjustedPc(0xa00U, invalid_elf));
+ EXPECT_EQ(0U, regs_mips64.GetPcAdjustment(0xa00U, invalid_elf));
}
TEST_F(RegsTest, arm_set_from_raw) {
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index df262f5..515bc8c 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -188,11 +188,19 @@
std::string frame_info(DumpFrames(unwinder));
ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
- " #00 pc 0001a9f8 libc.so (abort+63)\n"
+ " #00 pc 0001a9f8 libc.so (abort+64)\n"
" #01 pc 00006a1b libbase.so (_ZN7android4base14DefaultAborterEPKc+6)\n"
" #02 pc 00007441 libbase.so (_ZN7android4base10LogMessageD2Ev+748)\n"
" #03 pc 00015147 /does/not/exist/libhidlbase.so\n",
frame_info);
+ EXPECT_EQ(0xf31ea9f8U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xe9c866f8U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0xf2da0a1bU, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xe9c86728U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0xf2da1441U, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xe9c86730U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0xf3367147U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xe9c86778U, unwinder.frames()[3].sp);
}
TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
@@ -209,6 +217,10 @@
" #01 pc 0006dce5 libandroid_runtime.so "
"(_ZN7android14AndroidRuntime19javaCreateThreadEtcEPFiPvES1_PKcijPS1_)\n",
frame_info);
+ EXPECT_EQ(0xf1f6dc49U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xd8fe6930U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0xf1f6dce5U, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xd8fe6958U, unwinder.frames()[1].sp);
}
TEST_F(UnwindOfflineTest, pc_straddle_arm64) {
@@ -229,6 +241,18 @@
"(_ZN11unwindstack37UnwindTest_remote_through_signal_Test8TestBodyEv+32)\n"
" #05 pc 0000000000455d70 libunwindstack_test (_ZN7testing4Test3RunEv+392)\n",
frame_info);
+ EXPECT_EQ(0x64d09d4fd8U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7fe0d84040U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x64d09d5078U, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7fe0d84070U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x64d09d508cU, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7fe0d84080U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x64d09d88fcU, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7fe0d84090U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x64d09d88d8U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7fe0d840f0U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0x64d0a00d70U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0x7fe0d84110U, unwinder.frames()[5].sp);
}
static void AddMemory(std::string file_name, MemoryOfflineParts* parts) {
@@ -390,6 +414,144 @@
" #67 pc 00001a80 dalvikvm32 (main+1312)\n"
" #68 pc 00018275 libc.so\n",
frame_info);
+ EXPECT_EQ(0xeb89bfb8U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xffeb5280U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0xeb89af00U, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xffeb52a0U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0xec6061a8U, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xffeb5ce0U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0xee75be81U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xffeb5d30U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0xf728e4d2U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xffeb5d60U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0xf6d27ab5U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0xffeb5d80U, unwinder.frames()[5].sp);
+ EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[6].pc);
+ EXPECT_EQ(0xffeb5e20U, unwinder.frames()[6].sp);
+ EXPECT_EQ(0xf6f73552U, unwinder.frames()[7].pc);
+ EXPECT_EQ(0xffeb5ec0U, unwinder.frames()[7].sp);
+ EXPECT_EQ(0xf6f7499aU, unwinder.frames()[8].pc);
+ EXPECT_EQ(0xffeb5f40U, unwinder.frames()[8].sp);
+ EXPECT_EQ(0xf7265362U, unwinder.frames()[9].pc);
+ EXPECT_EQ(0xffeb5fb0U, unwinder.frames()[9].sp);
+ EXPECT_EQ(0xf72945bdU, unwinder.frames()[10].pc);
+ EXPECT_EQ(0xffeb6110U, unwinder.frames()[10].sp);
+ EXPECT_EQ(0xee75be04U, unwinder.frames()[11].pc);
+ EXPECT_EQ(0xffeb6160U, unwinder.frames()[11].sp);
+ EXPECT_EQ(0xf728e4d2U, unwinder.frames()[12].pc);
+ EXPECT_EQ(0xffeb6180U, unwinder.frames()[12].sp);
+ EXPECT_EQ(0xf6d27ab5U, unwinder.frames()[13].pc);
+ EXPECT_EQ(0xffeb61b0U, unwinder.frames()[13].sp);
+ EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[14].pc);
+ EXPECT_EQ(0xffeb6250U, unwinder.frames()[14].sp);
+ EXPECT_EQ(0xf6f73552U, unwinder.frames()[15].pc);
+ EXPECT_EQ(0xffeb62f0U, unwinder.frames()[15].sp);
+ EXPECT_EQ(0xf6f7499aU, unwinder.frames()[16].pc);
+ EXPECT_EQ(0xffeb6370U, unwinder.frames()[16].sp);
+ EXPECT_EQ(0xf7265362U, unwinder.frames()[17].pc);
+ EXPECT_EQ(0xffeb63e0U, unwinder.frames()[17].sp);
+ EXPECT_EQ(0xf72945bdU, unwinder.frames()[18].pc);
+ EXPECT_EQ(0xffeb6530U, unwinder.frames()[18].sp);
+ EXPECT_EQ(0xee75bd3cU, unwinder.frames()[19].pc);
+ EXPECT_EQ(0xffeb6580U, unwinder.frames()[19].sp);
+ EXPECT_EQ(0xf728e4d2U, unwinder.frames()[20].pc);
+ EXPECT_EQ(0xffeb65b0U, unwinder.frames()[20].sp);
+ EXPECT_EQ(0xf6d27ab5U, unwinder.frames()[21].pc);
+ EXPECT_EQ(0xffeb65e0U, unwinder.frames()[21].sp);
+ EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[22].pc);
+ EXPECT_EQ(0xffeb6680U, unwinder.frames()[22].sp);
+ EXPECT_EQ(0xf6f73552U, unwinder.frames()[23].pc);
+ EXPECT_EQ(0xffeb6720U, unwinder.frames()[23].sp);
+ EXPECT_EQ(0xf6f7499aU, unwinder.frames()[24].pc);
+ EXPECT_EQ(0xffeb67a0U, unwinder.frames()[24].sp);
+ EXPECT_EQ(0xf7265362U, unwinder.frames()[25].pc);
+ EXPECT_EQ(0xffeb6810U, unwinder.frames()[25].sp);
+ EXPECT_EQ(0xf72945bdU, unwinder.frames()[26].pc);
+ EXPECT_EQ(0xffeb6960U, unwinder.frames()[26].sp);
+ EXPECT_EQ(0xee75bbdcU, unwinder.frames()[27].pc);
+ EXPECT_EQ(0xffeb69b0U, unwinder.frames()[27].sp);
+ EXPECT_EQ(0xf728e6a2U, unwinder.frames()[28].pc);
+ EXPECT_EQ(0xffeb69f0U, unwinder.frames()[28].sp);
+ EXPECT_EQ(0xf6d27acbU, unwinder.frames()[29].pc);
+ EXPECT_EQ(0xffeb6a20U, unwinder.frames()[29].sp);
+ EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[30].pc);
+ EXPECT_EQ(0xffeb6ac0U, unwinder.frames()[30].sp);
+ EXPECT_EQ(0xf6f73552U, unwinder.frames()[31].pc);
+ EXPECT_EQ(0xffeb6b60U, unwinder.frames()[31].sp);
+ EXPECT_EQ(0xf6f7499aU, unwinder.frames()[32].pc);
+ EXPECT_EQ(0xffeb6be0U, unwinder.frames()[32].sp);
+ EXPECT_EQ(0xf7265362U, unwinder.frames()[33].pc);
+ EXPECT_EQ(0xffeb6c50U, unwinder.frames()[33].sp);
+ EXPECT_EQ(0xf72945bdU, unwinder.frames()[34].pc);
+ EXPECT_EQ(0xffeb6dd0U, unwinder.frames()[34].sp);
+ EXPECT_EQ(0xee75b625U, unwinder.frames()[35].pc);
+ EXPECT_EQ(0xffeb6e20U, unwinder.frames()[35].sp);
+ EXPECT_EQ(0xf728e4d2U, unwinder.frames()[36].pc);
+ EXPECT_EQ(0xffeb6e50U, unwinder.frames()[36].sp);
+ EXPECT_EQ(0xf6d27ab5U, unwinder.frames()[37].pc);
+ EXPECT_EQ(0xffeb6e70U, unwinder.frames()[37].sp);
+ EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[38].pc);
+ EXPECT_EQ(0xffeb6f10U, unwinder.frames()[38].sp);
+ EXPECT_EQ(0xf6f73552U, unwinder.frames()[39].pc);
+ EXPECT_EQ(0xffeb6fb0U, unwinder.frames()[39].sp);
+ EXPECT_EQ(0xf6f7499aU, unwinder.frames()[40].pc);
+ EXPECT_EQ(0xffeb7030U, unwinder.frames()[40].sp);
+ EXPECT_EQ(0xf7265362U, unwinder.frames()[41].pc);
+ EXPECT_EQ(0xffeb70a0U, unwinder.frames()[41].sp);
+ EXPECT_EQ(0xf72945bdU, unwinder.frames()[42].pc);
+ EXPECT_EQ(0xffeb71f0U, unwinder.frames()[42].sp);
+ EXPECT_EQ(0xee75aedcU, unwinder.frames()[43].pc);
+ EXPECT_EQ(0xffeb7240U, unwinder.frames()[43].sp);
+ EXPECT_EQ(0xf728e4d2U, unwinder.frames()[44].pc);
+ EXPECT_EQ(0xffeb72a0U, unwinder.frames()[44].sp);
+ EXPECT_EQ(0xf6d27ab5U, unwinder.frames()[45].pc);
+ EXPECT_EQ(0xffeb72c0U, unwinder.frames()[45].sp);
+ EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[46].pc);
+ EXPECT_EQ(0xffeb7360U, unwinder.frames()[46].sp);
+ EXPECT_EQ(0xf6f73552U, unwinder.frames()[47].pc);
+ EXPECT_EQ(0xffeb7400U, unwinder.frames()[47].sp);
+ EXPECT_EQ(0xf6f7499aU, unwinder.frames()[48].pc);
+ EXPECT_EQ(0xffeb7480U, unwinder.frames()[48].sp);
+ EXPECT_EQ(0xf7265362U, unwinder.frames()[49].pc);
+ EXPECT_EQ(0xffeb74f0U, unwinder.frames()[49].sp);
+ EXPECT_EQ(0xf72945bdU, unwinder.frames()[50].pc);
+ EXPECT_EQ(0xffeb7680U, unwinder.frames()[50].sp);
+ EXPECT_EQ(0xee756c22U, unwinder.frames()[51].pc);
+ EXPECT_EQ(0xffeb76d0U, unwinder.frames()[51].sp);
+ EXPECT_EQ(0xf728e6a2U, unwinder.frames()[52].pc);
+ EXPECT_EQ(0xffeb76f0U, unwinder.frames()[52].sp);
+ EXPECT_EQ(0xf6d27acbU, unwinder.frames()[53].pc);
+ EXPECT_EQ(0xffeb7710U, unwinder.frames()[53].sp);
+ EXPECT_EQ(0xf6f7df0dU, unwinder.frames()[54].pc);
+ EXPECT_EQ(0xffeb77b0U, unwinder.frames()[54].sp);
+ EXPECT_EQ(0xf6f73552U, unwinder.frames()[55].pc);
+ EXPECT_EQ(0xffeb7850U, unwinder.frames()[55].sp);
+ EXPECT_EQ(0xf6f7499aU, unwinder.frames()[56].pc);
+ EXPECT_EQ(0xffeb78d0U, unwinder.frames()[56].sp);
+ EXPECT_EQ(0xf7265362U, unwinder.frames()[57].pc);
+ EXPECT_EQ(0xffeb7940U, unwinder.frames()[57].sp);
+ EXPECT_EQ(0xf72945bdU, unwinder.frames()[58].pc);
+ EXPECT_EQ(0xffeb7a80U, unwinder.frames()[58].sp);
+ EXPECT_EQ(0xf728e6a2U, unwinder.frames()[59].pc);
+ EXPECT_EQ(0xffeb7ad0U, unwinder.frames()[59].sp);
+ EXPECT_EQ(0xf6d27acbU, unwinder.frames()[60].pc);
+ EXPECT_EQ(0xffeb7af0U, unwinder.frames()[60].sp);
+ EXPECT_EQ(0xf718bc95U, unwinder.frames()[61].pc);
+ EXPECT_EQ(0xffeb7b90U, unwinder.frames()[61].sp);
+ EXPECT_EQ(0xf718bb5aU, unwinder.frames()[62].pc);
+ EXPECT_EQ(0xffeb7c50U, unwinder.frames()[62].sp);
+ EXPECT_EQ(0xf706b3ddU, unwinder.frames()[63].pc);
+ EXPECT_EQ(0xffeb7d10U, unwinder.frames()[63].sp);
+ EXPECT_EQ(0xf6d6548cU, unwinder.frames()[64].pc);
+ EXPECT_EQ(0xffeb7d70U, unwinder.frames()[64].sp);
+ EXPECT_EQ(0xf6d5df06U, unwinder.frames()[65].pc);
+ EXPECT_EQ(0xffeb7df0U, unwinder.frames()[65].sp);
+ EXPECT_EQ(0x56574d8cU, unwinder.frames()[66].pc);
+ EXPECT_EQ(0xffeb7e40U, unwinder.frames()[66].sp);
+ EXPECT_EQ(0x56574a80U, unwinder.frames()[67].pc);
+ EXPECT_EQ(0xffeb7e70U, unwinder.frames()[67].sp);
+ EXPECT_EQ(0xf7363275U, unwinder.frames()[68].pc);
+ EXPECT_EQ(0xffeb7ef0U, unwinder.frames()[68].sp);
}
TEST_F(UnwindOfflineTest, jit_debug_arm) {
@@ -413,7 +575,7 @@
std::string frame_info(DumpFrames(unwinder));
ASSERT_EQ(76U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
- " #00 pc 00018a5e libarttestd.so (Java_Main_unwindInProcess+865)\n"
+ " #00 pc 00018a5e libarttestd.so (Java_Main_unwindInProcess+866)\n"
" #01 pc 0000212d (offset 0x2000) 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
"boolean)+92)\n"
" #02 pc 00011cb1 anonymous:e2796000 (boolean Main.bar(boolean)+72)\n"
@@ -553,6 +715,158 @@
" #74 pc 00001349 dalvikvm32 (main+896)\n"
" #75 pc 000850c9 libc.so\n",
frame_info);
+ EXPECT_EQ(0xdfe66a5eU, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xff85d180U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0xe044712dU, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xff85d200U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0xe27a7cb1U, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xff85d290U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0xed75c175U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xff85d2b0U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0xed761129U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xff85d2e8U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0xed3b97a9U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0xff85d370U, unwinder.frames()[5].sp);
+ EXPECT_EQ(0xed541833U, unwinder.frames()[6].pc);
+ EXPECT_EQ(0xff85d3d8U, unwinder.frames()[6].sp);
+ EXPECT_EQ(0xed528935U, unwinder.frames()[7].pc);
+ EXPECT_EQ(0xff85d428U, unwinder.frames()[7].sp);
+ EXPECT_EQ(0xed52971dU, unwinder.frames()[8].pc);
+ EXPECT_EQ(0xff85d470U, unwinder.frames()[8].sp);
+ EXPECT_EQ(0xed73c865U, unwinder.frames()[9].pc);
+ EXPECT_EQ(0xff85d4b0U, unwinder.frames()[9].sp);
+ EXPECT_EQ(0xed7606ffU, unwinder.frames()[10].pc);
+ EXPECT_EQ(0xff85d5d0U, unwinder.frames()[10].sp);
+ EXPECT_EQ(0xe27a7c31U, unwinder.frames()[11].pc);
+ EXPECT_EQ(0xff85d640U, unwinder.frames()[11].sp);
+ EXPECT_EQ(0xed75c175U, unwinder.frames()[12].pc);
+ EXPECT_EQ(0xff85d660U, unwinder.frames()[12].sp);
+ EXPECT_EQ(0xed761129U, unwinder.frames()[13].pc);
+ EXPECT_EQ(0xff85d698U, unwinder.frames()[13].sp);
+ EXPECT_EQ(0xed3b97a9U, unwinder.frames()[14].pc);
+ EXPECT_EQ(0xff85d720U, unwinder.frames()[14].sp);
+ EXPECT_EQ(0xed541833U, unwinder.frames()[15].pc);
+ EXPECT_EQ(0xff85d788U, unwinder.frames()[15].sp);
+ EXPECT_EQ(0xed528935U, unwinder.frames()[16].pc);
+ EXPECT_EQ(0xff85d7d8U, unwinder.frames()[16].sp);
+ EXPECT_EQ(0xed52971dU, unwinder.frames()[17].pc);
+ EXPECT_EQ(0xff85d820U, unwinder.frames()[17].sp);
+ EXPECT_EQ(0xed73c865U, unwinder.frames()[18].pc);
+ EXPECT_EQ(0xff85d860U, unwinder.frames()[18].sp);
+ EXPECT_EQ(0xed7606ffU, unwinder.frames()[19].pc);
+ EXPECT_EQ(0xff85d970U, unwinder.frames()[19].sp);
+ EXPECT_EQ(0xe27a7b77U, unwinder.frames()[20].pc);
+ EXPECT_EQ(0xff85d9e0U, unwinder.frames()[20].sp);
+ EXPECT_EQ(0xed75c175U, unwinder.frames()[21].pc);
+ EXPECT_EQ(0xff85da10U, unwinder.frames()[21].sp);
+ EXPECT_EQ(0xed761129U, unwinder.frames()[22].pc);
+ EXPECT_EQ(0xff85da48U, unwinder.frames()[22].sp);
+ EXPECT_EQ(0xed3b97a9U, unwinder.frames()[23].pc);
+ EXPECT_EQ(0xff85dad0U, unwinder.frames()[23].sp);
+ EXPECT_EQ(0xed541833U, unwinder.frames()[24].pc);
+ EXPECT_EQ(0xff85db38U, unwinder.frames()[24].sp);
+ EXPECT_EQ(0xed528935U, unwinder.frames()[25].pc);
+ EXPECT_EQ(0xff85db88U, unwinder.frames()[25].sp);
+ EXPECT_EQ(0xed52971dU, unwinder.frames()[26].pc);
+ EXPECT_EQ(0xff85dbd0U, unwinder.frames()[26].sp);
+ EXPECT_EQ(0xed73c865U, unwinder.frames()[27].pc);
+ EXPECT_EQ(0xff85dc10U, unwinder.frames()[27].sp);
+ EXPECT_EQ(0xed7606ffU, unwinder.frames()[28].pc);
+ EXPECT_EQ(0xff85dd20U, unwinder.frames()[28].sp);
+ EXPECT_EQ(0xe27a7a29U, unwinder.frames()[29].pc);
+ EXPECT_EQ(0xff85dd90U, unwinder.frames()[29].sp);
+ EXPECT_EQ(0xed75c175U, unwinder.frames()[30].pc);
+ EXPECT_EQ(0xff85ddc0U, unwinder.frames()[30].sp);
+ EXPECT_EQ(0xed76122fU, unwinder.frames()[31].pc);
+ EXPECT_EQ(0xff85de08U, unwinder.frames()[31].sp);
+ EXPECT_EQ(0xed3b97bbU, unwinder.frames()[32].pc);
+ EXPECT_EQ(0xff85de90U, unwinder.frames()[32].sp);
+ EXPECT_EQ(0xed541833U, unwinder.frames()[33].pc);
+ EXPECT_EQ(0xff85def8U, unwinder.frames()[33].sp);
+ EXPECT_EQ(0xed528935U, unwinder.frames()[34].pc);
+ EXPECT_EQ(0xff85df48U, unwinder.frames()[34].sp);
+ EXPECT_EQ(0xed52971dU, unwinder.frames()[35].pc);
+ EXPECT_EQ(0xff85df90U, unwinder.frames()[35].sp);
+ EXPECT_EQ(0xed73c865U, unwinder.frames()[36].pc);
+ EXPECT_EQ(0xff85dfd0U, unwinder.frames()[36].sp);
+ EXPECT_EQ(0xed7606ffU, unwinder.frames()[37].pc);
+ EXPECT_EQ(0xff85e110U, unwinder.frames()[37].sp);
+ EXPECT_EQ(0xe27a739bU, unwinder.frames()[38].pc);
+ EXPECT_EQ(0xff85e180U, unwinder.frames()[38].sp);
+ EXPECT_EQ(0xed75c175U, unwinder.frames()[39].pc);
+ EXPECT_EQ(0xff85e1b0U, unwinder.frames()[39].sp);
+ EXPECT_EQ(0xed761129U, unwinder.frames()[40].pc);
+ EXPECT_EQ(0xff85e1e0U, unwinder.frames()[40].sp);
+ EXPECT_EQ(0xed3b97a9U, unwinder.frames()[41].pc);
+ EXPECT_EQ(0xff85e268U, unwinder.frames()[41].sp);
+ EXPECT_EQ(0xed541833U, unwinder.frames()[42].pc);
+ EXPECT_EQ(0xff85e2d0U, unwinder.frames()[42].sp);
+ EXPECT_EQ(0xed528935U, unwinder.frames()[43].pc);
+ EXPECT_EQ(0xff85e320U, unwinder.frames()[43].sp);
+ EXPECT_EQ(0xed52971dU, unwinder.frames()[44].pc);
+ EXPECT_EQ(0xff85e368U, unwinder.frames()[44].sp);
+ EXPECT_EQ(0xed73c865U, unwinder.frames()[45].pc);
+ EXPECT_EQ(0xff85e3a8U, unwinder.frames()[45].sp);
+ EXPECT_EQ(0xed7606ffU, unwinder.frames()[46].pc);
+ EXPECT_EQ(0xff85e4c0U, unwinder.frames()[46].sp);
+ EXPECT_EQ(0xe27a6aa7U, unwinder.frames()[47].pc);
+ EXPECT_EQ(0xff85e530U, unwinder.frames()[47].sp);
+ EXPECT_EQ(0xed75c175U, unwinder.frames()[48].pc);
+ EXPECT_EQ(0xff85e5a0U, unwinder.frames()[48].sp);
+ EXPECT_EQ(0xed761129U, unwinder.frames()[49].pc);
+ EXPECT_EQ(0xff85e5d8U, unwinder.frames()[49].sp);
+ EXPECT_EQ(0xed3b97a9U, unwinder.frames()[50].pc);
+ EXPECT_EQ(0xff85e660U, unwinder.frames()[50].sp);
+ EXPECT_EQ(0xed541833U, unwinder.frames()[51].pc);
+ EXPECT_EQ(0xff85e6c8U, unwinder.frames()[51].sp);
+ EXPECT_EQ(0xed528935U, unwinder.frames()[52].pc);
+ EXPECT_EQ(0xff85e718U, unwinder.frames()[52].sp);
+ EXPECT_EQ(0xed52971dU, unwinder.frames()[53].pc);
+ EXPECT_EQ(0xff85e760U, unwinder.frames()[53].sp);
+ EXPECT_EQ(0xed73c865U, unwinder.frames()[54].pc);
+ EXPECT_EQ(0xff85e7a0U, unwinder.frames()[54].sp);
+ EXPECT_EQ(0xed7606ffU, unwinder.frames()[55].pc);
+ EXPECT_EQ(0xff85e8f0U, unwinder.frames()[55].sp);
+ EXPECT_EQ(0xe27a1a99U, unwinder.frames()[56].pc);
+ EXPECT_EQ(0xff85e960U, unwinder.frames()[56].sp);
+ EXPECT_EQ(0xed75c175U, unwinder.frames()[57].pc);
+ EXPECT_EQ(0xff85e990U, unwinder.frames()[57].sp);
+ EXPECT_EQ(0xed76122fU, unwinder.frames()[58].pc);
+ EXPECT_EQ(0xff85e9c8U, unwinder.frames()[58].sp);
+ EXPECT_EQ(0xed3b97bbU, unwinder.frames()[59].pc);
+ EXPECT_EQ(0xff85ea50U, unwinder.frames()[59].sp);
+ EXPECT_EQ(0xed541833U, unwinder.frames()[60].pc);
+ EXPECT_EQ(0xff85eab8U, unwinder.frames()[60].sp);
+ EXPECT_EQ(0xed528935U, unwinder.frames()[61].pc);
+ EXPECT_EQ(0xff85eb08U, unwinder.frames()[61].sp);
+ EXPECT_EQ(0xed52971dU, unwinder.frames()[62].pc);
+ EXPECT_EQ(0xff85eb50U, unwinder.frames()[62].sp);
+ EXPECT_EQ(0xed73c865U, unwinder.frames()[63].pc);
+ EXPECT_EQ(0xff85eb90U, unwinder.frames()[63].sp);
+ EXPECT_EQ(0xed7606ffU, unwinder.frames()[64].pc);
+ EXPECT_EQ(0xff85ec90U, unwinder.frames()[64].sp);
+ EXPECT_EQ(0xed75c175U, unwinder.frames()[65].pc);
+ EXPECT_EQ(0xff85ed00U, unwinder.frames()[65].sp);
+ EXPECT_EQ(0xed76122fU, unwinder.frames()[66].pc);
+ EXPECT_EQ(0xff85ed38U, unwinder.frames()[66].sp);
+ EXPECT_EQ(0xed3b97bbU, unwinder.frames()[67].pc);
+ EXPECT_EQ(0xff85edc0U, unwinder.frames()[67].sp);
+ EXPECT_EQ(0xed6ac92dU, unwinder.frames()[68].pc);
+ EXPECT_EQ(0xff85ee28U, unwinder.frames()[68].sp);
+ EXPECT_EQ(0xed6ac6c3U, unwinder.frames()[69].pc);
+ EXPECT_EQ(0xff85eeb8U, unwinder.frames()[69].sp);
+ EXPECT_EQ(0xed602411U, unwinder.frames()[70].pc);
+ EXPECT_EQ(0xff85ef48U, unwinder.frames()[70].sp);
+ EXPECT_EQ(0xed3e0a9fU, unwinder.frames()[71].pc);
+ EXPECT_EQ(0xff85ef90U, unwinder.frames()[71].sp);
+ EXPECT_EQ(0xed3db9b9U, unwinder.frames()[72].pc);
+ EXPECT_EQ(0xff85f008U, unwinder.frames()[72].sp);
+ EXPECT_EQ(0xab0d459fU, unwinder.frames()[73].pc);
+ EXPECT_EQ(0xff85f038U, unwinder.frames()[73].sp);
+ EXPECT_EQ(0xab0d4349U, unwinder.frames()[74].pc);
+ EXPECT_EQ(0xff85f050U, unwinder.frames()[74].sp);
+ EXPECT_EQ(0xedb0d0c9U, unwinder.frames()[75].pc);
+ EXPECT_EQ(0xff85f0c0U, unwinder.frames()[75].sp);
}
// The eh_frame_hdr data is present but set to zero fdes. This should
@@ -573,6 +887,16 @@
" #03 pc 0000000000000590 waiter64\n"
" #04 pc 00000000000a8e98 libc.so (__libc_init+88)\n",
frame_info);
+ EXPECT_EQ(0x60a9fdf550U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7fdd141990U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x60a9fdf568U, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7fdd1419a0U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x60a9fdf57cU, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7fdd1419b0U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x60a9fdf590U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7fdd1419c0U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x7542d68e98U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7fdd1419d0U, unwinder.frames()[4].sp);
}
// The elf has bad eh_frame unwind information for the pcs. If eh_frame
@@ -592,6 +916,16 @@
" #03 pc 000006f7 waiter (main+23)\n"
" #04 pc 00018275 libc.so\n",
frame_info);
+ EXPECT_EQ(0x56598685U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xffcf9e38U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x565986b7U, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xffcf9e50U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x565986d7U, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xffcf9e60U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x565986f7U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xffcf9e70U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0xf744a275U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xffcf9e80U, unwinder.frames()[4].sp);
}
// Make sure that a pc that is at the beginning of an fde unwinds correctly.
@@ -610,6 +944,16 @@
" #03 pc 00000000000013ed unwind_test64 (main+13)\n"
" #04 pc 00000000000202b0 libc.so\n",
frame_info);
+ EXPECT_EQ(0x561550b17a80U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0x7ffcc8596ce8U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0x561550b17dd9U, unwinder.frames()[1].pc);
+ EXPECT_EQ(0x7ffcc8596cf0U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0x561550b1821eU, unwinder.frames()[2].pc);
+ EXPECT_EQ(0x7ffcc8596f40U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0x561550b183edU, unwinder.frames()[3].pc);
+ EXPECT_EQ(0x7ffcc8597190U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0x7f4de62162b0U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0x7ffcc85971a0U, unwinder.frames()[4].sp);
}
} // namespace unwindstack
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index a0abcca..5a8edfd 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -53,8 +53,7 @@
printf(" PC 0x%" PRIx64, addr + load_bias);
uint64_t func_offset;
uint64_t pc = addr + load_bias;
- // This might be a thumb function, so set the low bit.
- if (interface->GetFunctionName(pc | 1, load_bias, &name, &func_offset) && !name.empty()) {
+ if (interface->GetFunctionName(pc, load_bias, &name, &func_offset) && !name.empty()) {
printf(" <%s>", name.c_str());
}
printf("\n");
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 209bf9a..0d7925a 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -129,7 +129,6 @@
"PropertyMap.cpp",
"RefBase.cpp",
"SharedBuffer.cpp",
- "Static.cpp",
"StopWatch.cpp",
"String8.cpp",
"String16.cpp",
@@ -160,7 +159,6 @@
cc_library {
name: "libutilscallstack",
defaults: ["libutils_defaults"],
- vendor_available: false,
srcs: [
"CallStack.cpp",
@@ -201,5 +199,3 @@
"-Werror",
],
}
-
-subdirs = ["tests"]
diff --git a/libutils/Static.cpp b/libutils/Static.cpp
deleted file mode 100644
index 3ed07a1..0000000
--- a/libutils/Static.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-// All static variables go here, to control initialization and
-// destruction order in the library.
-
-namespace android {
-
-// For String8.cpp
-extern void initialize_string8();
-extern void terminate_string8();
-
-// For String16.cpp
-extern void initialize_string16();
-extern void terminate_string16();
-
-class LibUtilsFirstStatics
-{
-public:
- LibUtilsFirstStatics()
- {
- initialize_string8();
- initialize_string16();
- }
-
- ~LibUtilsFirstStatics()
- {
- terminate_string16();
- terminate_string8();
- }
-};
-
-static LibUtilsFirstStatics gFirstStatics;
-int gDarwinCantLoadAllObjects = 1;
-
-} // namespace android
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index ad335c3..84d53dd 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -24,29 +24,16 @@
namespace android {
-static SharedBuffer* gEmptyStringBuf = NULL;
-static char16_t* gEmptyString = NULL;
+static inline char16_t* getEmptyString() {
+ static SharedBuffer* gEmptyStringBuf = [] {
+ SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t));
+ char16_t* str = static_cast<char16_t*>(buf->data());
+ *str = 0;
+ return buf;
+ }();
-static inline char16_t* getEmptyString()
-{
gEmptyStringBuf->acquire();
- return gEmptyString;
-}
-
-void initialize_string16()
-{
- SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t));
- char16_t* str = (char16_t*)buf->data();
- *str = 0;
- gEmptyStringBuf = buf;
- gEmptyString = str;
-}
-
-void terminate_string16()
-{
- SharedBuffer::bufferFromData(gEmptyString)->release();
- gEmptyStringBuf = NULL;
- gEmptyString = NULL;
+ return static_cast<char16_t*>(gEmptyStringBuf->data());
}
// ---------------------------------------------------------------------------
diff --git a/libutils/String8.cpp b/libutils/String8.cpp
index ad0e72e..580e870 100644
--- a/libutils/String8.cpp
+++ b/libutils/String8.cpp
@@ -40,40 +40,16 @@
// to OS_PATH_SEPARATOR.
#define RES_PATH_SEPARATOR '/'
-static SharedBuffer* gEmptyStringBuf = NULL;
-static char* gEmptyString = NULL;
+static inline char* getEmptyString() {
+ static SharedBuffer* gEmptyStringBuf = [] {
+ SharedBuffer* buf = SharedBuffer::alloc(1);
+ char* str = static_cast<char*>(buf->data());
+ *str = 0;
+ return buf;
+ }();
-extern int gDarwinCantLoadAllObjects;
-int gDarwinIsReallyAnnoying;
-
-void initialize_string8();
-
-static inline char* getEmptyString()
-{
gEmptyStringBuf->acquire();
- return gEmptyString;
-}
-
-void initialize_string8()
-{
- // HACK: This dummy dependency forces linking libutils Static.cpp,
- // which is needed to initialize String8/String16 classes.
- // These variables are named for Darwin, but are needed elsewhere too,
- // including static linking on any platform.
- gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects;
-
- SharedBuffer* buf = SharedBuffer::alloc(1);
- char* str = (char*)buf->data();
- *str = 0;
- gEmptyStringBuf = buf;
- gEmptyString = str;
-}
-
-void terminate_string8()
-{
- SharedBuffer::bufferFromData(gEmptyString)->release();
- gEmptyStringBuf = NULL;
- gEmptyString = NULL;
+ return static_cast<char*>(gEmptyStringBuf->data());
}
// ---------------------------------------------------------------------------
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index f9f8c73..5e5e7af 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -224,7 +224,9 @@
return kInvalidOffset;
}
if (eocd->num_records == 0) {
+#if defined(__ANDROID__)
ALOGW("Zip: empty archive?");
+#endif
return kEmptyArchive;
}
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
index 3f8a503..d172755 100644
--- a/lmkd/Android.bp
+++ b/lmkd/Android.bp
@@ -4,10 +4,32 @@
srcs: ["lmkd.c"],
shared_libs: [
"liblog",
- "libprocessgroup",
"libcutils",
],
+ local_include_dirs: ["include"],
cflags: ["-Werror"],
init_rc: ["lmkd.rc"],
+
+ product_variables: {
+ debuggable: {
+ cflags: [
+ "-DLMKD_TRACE_KILLS"
+ ],
+ },
+ },
+}
+
+cc_library_static {
+ name: "liblmkd_utils",
+ srcs: ["liblmkd_utils.c"],
+ shared_libs: [
+ "libcutils",
+ ],
+ export_include_dirs: ["include"],
+ cppflags: [
+ "-g",
+ "-Wall",
+ "-Werror",
+ ]
}
diff --git a/lmkd/include/liblmkd_utils.h b/lmkd/include/liblmkd_utils.h
new file mode 100644
index 0000000..72e3f4a
--- /dev/null
+++ b/lmkd/include/liblmkd_utils.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 Google, Inc
+ *
+ * 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 _LIBLMKD_UTILS_H_
+#define _LIBLMKD_UTILS_H_
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <lmkd.h>
+
+__BEGIN_DECLS
+
+/*
+ * Connects to lmkd process and returns socket handle.
+ * On success returns socket handle.
+ * On error, -1 is returned, and errno is set appropriately.
+ */
+int lmkd_connect();
+
+/*
+ * Registers a process with lmkd and sets its oomadj score.
+ * On success returns 0.
+ * On error, -1 is returned.
+ * In the case of error errno is set appropriately.
+ */
+int lmkd_register_proc(int sock, struct lmk_procprio *params);
+
+/*
+ * Creates memcg directory for given process.
+ * On success returns 0.
+ * -1 is returned if path creation failed.
+ * -2 is returned if tasks file open operation failed.
+ * -3 is returned if tasks file write operation failed.
+ * In the case of error errno is set appropriately.
+ */
+int create_memcg(uid_t uid, pid_t pid);
+
+__END_DECLS
+
+#endif /* _LIBLMKD_UTILS_H_ */
diff --git a/lmkd/include/lmkd.h b/lmkd/include/lmkd.h
new file mode 100644
index 0000000..fe6364d
--- /dev/null
+++ b/lmkd/include/lmkd.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2018 Google, Inc
+ *
+ * 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 _LMKD_H_
+#define _LMKD_H_
+
+#include <arpa/inet.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+/*
+ * Supported LMKD commands
+ */
+enum lmk_cmd {
+ LMK_TARGET = 0, /* Associate minfree with oom_adj_score */
+ LMK_PROCPRIO, /* Register a process and set its oom_adj_score */
+ LMK_PROCREMOVE, /* Unregister a process */
+};
+
+/*
+ * Max number of targets in LMK_TARGET command.
+ */
+#define MAX_TARGETS 6
+
+/*
+ * Max packet length in bytes.
+ * Longest packet is LMK_TARGET followed by MAX_TARGETS
+ * of minfree and oom_adj_score values
+ */
+#define CTRL_PACKET_MAX_SIZE (sizeof(int) * (MAX_TARGETS * 2 + 1))
+
+/* LMKD packet - first int is lmk_cmd followed by payload */
+typedef int LMKD_CTRL_PACKET[CTRL_PACKET_MAX_SIZE / sizeof(int)];
+
+/* Get LMKD packet command */
+inline enum lmk_cmd lmkd_pack_get_cmd(LMKD_CTRL_PACKET pack) {
+ return (enum lmk_cmd)ntohl(pack[0]);
+}
+
+/* LMK_TARGET packet payload */
+struct lmk_target {
+ int minfree;
+ int oom_adj_score;
+};
+
+/*
+ * For LMK_TARGET packet get target_idx-th payload.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline void lmkd_pack_get_target(LMKD_CTRL_PACKET packet,
+ int target_idx, struct lmk_target *target) {
+ target->minfree = ntohl(packet[target_idx * 2 + 1]);
+ target->oom_adj_score = ntohl(packet[target_idx * 2 + 2]);
+}
+
+/*
+ * Prepare LMK_TARGET packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_target(LMKD_CTRL_PACKET packet,
+ struct lmk_target *targets,
+ size_t target_cnt) {
+ int idx = 0;
+ packet[idx++] = htonl(LMK_TARGET);
+ while (target_cnt) {
+ packet[idx++] = htonl(targets->minfree);
+ packet[idx++] = htonl(targets->oom_adj_score);
+ targets++;
+ target_cnt--;
+ }
+ return idx * sizeof(int);
+}
+
+/* LMK_PROCPRIO packet payload */
+struct lmk_procprio {
+ pid_t pid;
+ uid_t uid;
+ int oomadj;
+};
+
+/*
+ * For LMK_PROCPRIO packet get its payload.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline void lmkd_pack_get_procprio(LMKD_CTRL_PACKET packet,
+ struct lmk_procprio *params) {
+ params->pid = (pid_t)ntohl(packet[1]);
+ params->uid = (uid_t)ntohl(packet[2]);
+ params->oomadj = ntohl(packet[3]);
+}
+
+/*
+ * Prepare LMK_PROCPRIO packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_procprio(LMKD_CTRL_PACKET packet,
+ struct lmk_procprio *params) {
+ packet[0] = htonl(LMK_PROCPRIO);
+ packet[1] = htonl(params->pid);
+ packet[2] = htonl(params->uid);
+ packet[3] = htonl(params->oomadj);
+ return 4 * sizeof(int);
+}
+
+/* LMK_PROCREMOVE packet payload */
+struct lmk_procremove {
+ pid_t pid;
+};
+
+/*
+ * For LMK_PROCREMOVE packet get its payload.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline void lmkd_pack_get_procremove(LMKD_CTRL_PACKET packet,
+ struct lmk_procremove *params) {
+ params->pid = (pid_t)ntohl(packet[1]);
+}
+
+/*
+ * Prepare LMK_PROCREMOVE packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_procremove(LMKD_CTRL_PACKET packet,
+ struct lmk_procprio *params) {
+ packet[0] = htonl(LMK_PROCREMOVE);
+ packet[1] = htonl(params->pid);
+ return 2 * sizeof(int);
+}
+
+__END_DECLS
+
+#endif /* _LMKD_H_ */
diff --git a/lmkd/liblmkd_utils.c b/lmkd/liblmkd_utils.c
new file mode 100644
index 0000000..fa3b7a9
--- /dev/null
+++ b/lmkd/liblmkd_utils.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2018 Google, Inc
+ *
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <sys/cdefs.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <liblmkd_utils.h>
+#include <cutils/sockets.h>
+
+int lmkd_connect() {
+ return socket_local_client("lmkd",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_SEQPACKET);
+}
+
+int lmkd_register_proc(int sock, struct lmk_procprio *params) {
+ LMKD_CTRL_PACKET packet;
+ size_t size;
+ int ret;
+
+ size = lmkd_pack_set_procprio(packet, params);
+ ret = TEMP_FAILURE_RETRY(write(sock, packet, size));
+
+ return (ret < 0) ? -1 : 0;
+}
+
+int create_memcg(uid_t uid, pid_t pid) {
+ char buf[256];
+ int tasks_file;
+ int written;
+
+ snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u", uid);
+ if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
+ errno != EEXIST) {
+ return -1;
+ }
+
+ snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u", uid, pid);
+ if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
+ errno != EEXIST) {
+ return -1;
+ }
+
+ snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u/tasks", uid, pid);
+ tasks_file = open(buf, O_WRONLY);
+ if (tasks_file < 0) {
+ return -2;
+ }
+ written = snprintf(buf, sizeof(buf), "%u", pid);
+ if (__predict_false(written >= (int)sizeof(buf))) {
+ written = sizeof(buf) - 1;
+ }
+ written = TEMP_FAILURE_RETRY(write(tasks_file, buf, written));
+ close(tasks_file);
+
+ return (written < 0) ? -3 : 0;
+}
+
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 15471e0..45fa863 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -16,7 +16,6 @@
#define LOG_TAG "lowmemorykiller"
-#include <arpa/inet.h>
#include <errno.h>
#include <inttypes.h>
#include <sched.h>
@@ -29,13 +28,32 @@
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
-#include <time.h>
+#include <sys/sysinfo.h>
#include <unistd.h>
#include <cutils/properties.h>
#include <cutils/sockets.h>
+#include <lmkd.h>
#include <log/log.h>
-#include <processgroup/processgroup.h>
+
+/*
+ * Define LMKD_TRACE_KILLS to record lmkd kills in kernel traces
+ * to profile and correlate with OOM kills
+ */
+#ifdef LMKD_TRACE_KILLS
+
+#define ATRACE_TAG ATRACE_TAG_ALWAYS
+#include <cutils/trace.h>
+
+#define TRACE_KILL_START(pid) ATRACE_INT(__FUNCTION__, pid);
+#define TRACE_KILL_END() ATRACE_INT(__FUNCTION__, 0);
+
+#else /* LMKD_TRACE_KILLS */
+
+#define TRACE_KILL_START(pid)
+#define TRACE_KILL_END()
+
+#endif /* LMKD_TRACE_KILLS */
#ifndef __unused
#define __unused __attribute__((__unused__))
@@ -44,8 +62,6 @@
#define MEMCG_SYSFS_PATH "/dev/memcg/"
#define MEMCG_MEMORY_USAGE "/dev/memcg/memory.usage_in_bytes"
#define MEMCG_MEMORYSW_USAGE "/dev/memcg/memory.memsw.usage_in_bytes"
-#define MEMPRESSURE_WATCH_MEDIUM_LEVEL "medium"
-#define MEMPRESSURE_WATCH_CRITICAL_LEVEL "critical"
#define ZONEINFO_PATH "/proc/zoneinfo"
#define LINE_MAX 128
@@ -55,43 +71,68 @@
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
#define EIGHT_MEGA (1 << 23)
-enum lmk_cmd {
- LMK_TARGET,
- LMK_PROCPRIO,
- LMK_PROCREMOVE,
-};
-
-#define MAX_TARGETS 6
-/*
- * longest is LMK_TARGET followed by MAX_TARGETS each minfree and minkillprio
- * values
- */
-#define CTRL_PACKET_MAX (sizeof(int) * (MAX_TARGETS * 2 + 1))
-
/* default to old in-kernel interface if no memory pressure events */
static int use_inkernel_interface = 1;
static bool has_inkernel_module;
-/* memory pressure level medium event */
-static int mpevfd[2];
-#define CRITICAL_INDEX 1
-#define MEDIUM_INDEX 0
+/* memory pressure levels */
+enum vmpressure_level {
+ VMPRESS_LEVEL_LOW = 0,
+ VMPRESS_LEVEL_MEDIUM,
+ VMPRESS_LEVEL_CRITICAL,
+ VMPRESS_LEVEL_COUNT
+};
-static int medium_oomadj;
-static int critical_oomadj;
+static const char *level_name[] = {
+ "low",
+ "medium",
+ "critical"
+};
+
+struct mem_size {
+ int free_mem;
+ int free_swap;
+};
+
+struct {
+ int min_free; /* recorded but not used yet */
+ int max_free;
+} low_pressure_mem = { -1, -1 };
+
+static int level_oomadj[VMPRESS_LEVEL_COUNT];
+static int mpevfd[VMPRESS_LEVEL_COUNT] = { -1, -1, -1 };
static bool debug_process_killing;
static bool enable_pressure_upgrade;
static int64_t upgrade_pressure;
static int64_t downgrade_pressure;
static bool is_go_device;
+static bool kill_heaviest_task;
+static unsigned long kill_timeout_ms;
-/* control socket listen and data */
-static int ctrl_lfd;
-static int ctrl_dfd = -1;
-static int ctrl_dfd_reopened; /* did we reopen ctrl conn on this loop? */
+/* data required to handle events */
+struct event_handler_info {
+ int data;
+ void (*handler)(int data, uint32_t events);
+};
-/* 2 memory pressure levels, 1 ctrl listen socket, 1 ctrl data socket */
-#define MAX_EPOLL_EVENTS 4
+/* data required to handle socket events */
+struct sock_event_handler_info {
+ int sock;
+ struct event_handler_info handler_info;
+};
+
+/* max supported number of data connections */
+#define MAX_DATA_CONN 2
+
+/* socket event handler data */
+static struct sock_event_handler_info ctrl_sock;
+static struct sock_event_handler_info data_sock[MAX_DATA_CONN];
+
+/* vmpressure event handler data */
+static struct event_handler_info vmpressure_hinfo[VMPRESS_LEVEL_COUNT];
+
+/* 3 memory pressure levels, 1 ctrl listen socket, 2 ctrl data socket */
+#define MAX_EPOLL_EVENTS (1 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT)
static int epollfd;
static int maxevents;
@@ -226,7 +267,7 @@
return 0;
}
-static void writefilestring(char *path, char *s) {
+static void writefilestring(const char *path, char *s) {
int fd = open(path, O_WRONLY | O_CLOEXEC);
int len = strlen(s);
int ret;
@@ -246,45 +287,49 @@
close(fd);
}
-static void cmd_procprio(int pid, int uid, int oomadj) {
+static void cmd_procprio(LMKD_CTRL_PACKET packet) {
struct proc *procp;
char path[80];
char val[20];
int soft_limit_mult;
+ struct lmk_procprio params;
- if (oomadj < OOM_SCORE_ADJ_MIN || oomadj > OOM_SCORE_ADJ_MAX) {
- ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj);
+ lmkd_pack_get_procprio(packet, ¶ms);
+
+ if (params.oomadj < OOM_SCORE_ADJ_MIN ||
+ params.oomadj > OOM_SCORE_ADJ_MAX) {
+ ALOGE("Invalid PROCPRIO oomadj argument %d", params.oomadj);
return;
}
- snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid);
- snprintf(val, sizeof(val), "%d", oomadj);
+ snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", params.pid);
+ snprintf(val, sizeof(val), "%d", params.oomadj);
writefilestring(path, val);
if (use_inkernel_interface)
return;
- if (oomadj >= 900) {
+ if (params.oomadj >= 900) {
soft_limit_mult = 0;
- } else if (oomadj >= 800) {
+ } else if (params.oomadj >= 800) {
soft_limit_mult = 0;
- } else if (oomadj >= 700) {
+ } else if (params.oomadj >= 700) {
soft_limit_mult = 0;
- } else if (oomadj >= 600) {
+ } else if (params.oomadj >= 600) {
// Launcher should be perceptible, don't kill it.
- oomadj = 200;
+ params.oomadj = 200;
soft_limit_mult = 1;
- } else if (oomadj >= 500) {
+ } else if (params.oomadj >= 500) {
soft_limit_mult = 0;
- } else if (oomadj >= 400) {
+ } else if (params.oomadj >= 400) {
soft_limit_mult = 0;
- } else if (oomadj >= 300) {
+ } else if (params.oomadj >= 300) {
soft_limit_mult = 1;
- } else if (oomadj >= 200) {
+ } else if (params.oomadj >= 200) {
soft_limit_mult = 2;
- } else if (oomadj >= 100) {
+ } else if (params.oomadj >= 100) {
soft_limit_mult = 10;
- } else if (oomadj >= 0) {
+ } else if (params.oomadj >= 0) {
soft_limit_mult = 20;
} else {
// Persistent processes will have a large
@@ -292,11 +337,13 @@
soft_limit_mult = 64;
}
- snprintf(path, sizeof(path), "/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes", uid, pid);
+ snprintf(path, sizeof(path),
+ "/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes",
+ params.uid, params.pid);
snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA);
writefilestring(path, val);
- procp = pid_lookup(pid);
+ procp = pid_lookup(params.pid);
if (!procp) {
procp = malloc(sizeof(struct proc));
if (!procp) {
@@ -304,33 +351,38 @@
return;
}
- procp->pid = pid;
- procp->uid = uid;
- procp->oomadj = oomadj;
+ procp->pid = params.pid;
+ procp->uid = params.uid;
+ procp->oomadj = params.oomadj;
proc_insert(procp);
} else {
proc_unslot(procp);
- procp->oomadj = oomadj;
+ procp->oomadj = params.oomadj;
proc_slot(procp);
}
}
-static void cmd_procremove(int pid) {
+static void cmd_procremove(LMKD_CTRL_PACKET packet) {
+ struct lmk_procremove params;
+
if (use_inkernel_interface)
return;
- pid_remove(pid);
+ lmkd_pack_get_procremove(packet, ¶ms);
+ pid_remove(params.pid);
}
-static void cmd_target(int ntargets, int *params) {
+static void cmd_target(int ntargets, LMKD_CTRL_PACKET packet) {
int i;
+ struct lmk_target target;
if (ntargets > (int)ARRAY_SIZE(lowmem_adj))
return;
for (i = 0; i < ntargets; i++) {
- lowmem_minfree[i] = ntohl(*params++);
- lowmem_adj[i] = ntohl(*params++);
+ lmkd_pack_get_target(packet, i, &target);
+ lowmem_minfree[i] = target.minfree;
+ lowmem_adj[i] = target.oom_adj_score;
}
lowmem_targets_size = ntargets;
@@ -361,17 +413,24 @@
}
}
-static void ctrl_data_close(void) {
- ALOGI("Closing Activity Manager data connection");
- close(ctrl_dfd);
- ctrl_dfd = -1;
+static void ctrl_data_close(int dsock_idx) {
+ struct epoll_event epev;
+
+ ALOGI("closing lmkd data connection");
+ if (epoll_ctl(epollfd, EPOLL_CTL_DEL, data_sock[dsock_idx].sock, &epev) == -1) {
+ // Log a warning and keep going
+ ALOGW("epoll_ctl for data connection socket failed; errno=%d", errno);
+ }
maxevents--;
+
+ close(data_sock[dsock_idx].sock);
+ data_sock[dsock_idx].sock = -1;
}
-static int ctrl_data_read(char *buf, size_t bufsz) {
+static int ctrl_data_read(int dsock_idx, char *buf, size_t bufsz) {
int ret = 0;
- ret = read(ctrl_dfd, buf, bufsz);
+ ret = read(data_sock[dsock_idx].sock, buf, bufsz);
if (ret == -1) {
ALOGE("control data socket read failed; errno=%d", errno);
@@ -383,39 +442,43 @@
return ret;
}
-static void ctrl_command_handler(void) {
- int ibuf[CTRL_PACKET_MAX / sizeof(int)];
+static void ctrl_command_handler(int dsock_idx) {
+ LMKD_CTRL_PACKET packet;
int len;
- int cmd = -1;
+ enum lmk_cmd cmd;
int nargs;
int targets;
- len = ctrl_data_read((char *)ibuf, CTRL_PACKET_MAX);
+ len = ctrl_data_read(dsock_idx, (char *)packet, CTRL_PACKET_MAX_SIZE);
if (len <= 0)
return;
+ if (len < (int)sizeof(int)) {
+ ALOGE("Wrong control socket read length len=%d", len);
+ return;
+ }
+
+ cmd = lmkd_pack_get_cmd(packet);
nargs = len / sizeof(int) - 1;
if (nargs < 0)
goto wronglen;
- cmd = ntohl(ibuf[0]);
-
switch(cmd) {
case LMK_TARGET:
targets = nargs / 2;
if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj))
goto wronglen;
- cmd_target(targets, &ibuf[1]);
+ cmd_target(targets, packet);
break;
case LMK_PROCPRIO:
if (nargs != 3)
goto wronglen;
- cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]), ntohl(ibuf[3]));
+ cmd_procprio(packet);
break;
case LMK_PROCREMOVE:
if (nargs != 1)
goto wronglen;
- cmd_procremove(ntohl(ibuf[1]));
+ cmd_procremove(packet);
break;
default:
ALOGE("Received unknown command code %d", cmd);
@@ -428,40 +491,57 @@
ALOGE("Wrong control socket read length cmd=%d len=%d", cmd, len);
}
-static void ctrl_data_handler(uint32_t events) {
- if (events & EPOLLHUP) {
- ALOGI("ActivityManager disconnected");
- if (!ctrl_dfd_reopened)
- ctrl_data_close();
- } else if (events & EPOLLIN) {
- ctrl_command_handler();
+static void ctrl_data_handler(int data, uint32_t events) {
+ if (events & EPOLLIN) {
+ ctrl_command_handler(data);
}
}
-static void ctrl_connect_handler(uint32_t events __unused) {
- struct epoll_event epev;
+static int get_free_dsock() {
+ for (int i = 0; i < MAX_DATA_CONN; i++) {
+ if (data_sock[i].sock < 0) {
+ return i;
+ }
+ }
+ return -1;
+}
- if (ctrl_dfd >= 0) {
- ctrl_data_close();
- ctrl_dfd_reopened = 1;
+static void ctrl_connect_handler(int data __unused, uint32_t events __unused) {
+ struct epoll_event epev;
+ int free_dscock_idx = get_free_dsock();
+
+ if (free_dscock_idx < 0) {
+ /*
+ * Number of data connections exceeded max supported. This should not
+ * happen but if it does we drop all existing connections and accept
+ * the new one. This prevents inactive connections from monopolizing
+ * data socket and if we drop ActivityManager connection it will
+ * immediately reconnect.
+ */
+ for (int i = 0; i < MAX_DATA_CONN; i++) {
+ ctrl_data_close(i);
+ }
+ free_dscock_idx = 0;
}
- ctrl_dfd = accept(ctrl_lfd, NULL, NULL);
-
- if (ctrl_dfd < 0) {
+ data_sock[free_dscock_idx].sock = accept(ctrl_sock.sock, NULL, NULL);
+ if (data_sock[free_dscock_idx].sock < 0) {
ALOGE("lmkd control socket accept failed; errno=%d", errno);
return;
}
- ALOGI("ActivityManager connected");
- maxevents++;
+ ALOGI("lmkd data connection established");
+ /* use data to store data connection idx */
+ data_sock[free_dscock_idx].handler_info.data = free_dscock_idx;
+ data_sock[free_dscock_idx].handler_info.handler = ctrl_data_handler;
epev.events = EPOLLIN;
- epev.data.ptr = (void *)ctrl_data_handler;
- if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_dfd, &epev) == -1) {
+ epev.data.ptr = (void *)&(data_sock[free_dscock_idx].handler_info);
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, data_sock[free_dscock_idx].sock, &epev) == -1) {
ALOGE("epoll_ctl for data connection socket failed; errno=%d", errno);
- ctrl_data_close();
+ ctrl_data_close(free_dscock_idx);
return;
}
+ maxevents++;
}
static int zoneinfo_parse_protection(char *cp) {
@@ -534,6 +614,18 @@
return 0;
}
+static int get_free_memory(struct mem_size *ms) {
+ struct sysinfo si;
+
+ if (sysinfo(&si) < 0)
+ return -1;
+
+ ms->free_mem = (int)(si.freeram * si.mem_unit / PAGE_SIZE);
+ ms->free_swap = (int)(si.freeswap * si.mem_unit / PAGE_SIZE);
+
+ return 0;
+}
+
static int proc_get_size(int pid) {
char path[PATH_MAX];
char line[LINE_MAX];
@@ -586,8 +678,32 @@
return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
}
+static struct proc *proc_get_heaviest(int oomadj) {
+ struct adjslot_list *head = &procadjslot_list[ADJTOSLOT(oomadj)];
+ struct adjslot_list *curr = head->next;
+ struct proc *maxprocp = NULL;
+ int maxsize = 0;
+ while (curr != head) {
+ int pid = ((struct proc *)curr)->pid;
+ int tasksize = proc_get_size(pid);
+ if (tasksize <= 0) {
+ struct adjslot_list *next = curr->next;
+ pid_remove(pid);
+ curr = next;
+ } else {
+ if (tasksize > maxsize) {
+ maxsize = tasksize;
+ maxprocp = (struct proc *)curr;
+ }
+ curr = curr->next;
+ }
+ }
+ return maxprocp;
+}
+
/* Kill one process specified by procp. Returns the size of the process killed */
-static int kill_one_process(struct proc* procp, int min_score_adj, bool is_critical) {
+static int kill_one_process(struct proc* procp, int min_score_adj,
+ enum vmpressure_level level) {
int pid = procp->pid;
uid_t uid = procp->uid;
char *taskname;
@@ -606,14 +722,18 @@
return -1;
}
+ TRACE_KILL_START(pid);
+
+ r = kill(pid, SIGKILL);
ALOGI(
"Killing '%s' (%d), uid %d, adj %d\n"
" to free %ldkB because system is under %s memory pressure oom_adj %d\n",
- taskname, pid, uid, procp->oomadj, tasksize * page_k, is_critical ? "critical" : "medium",
- min_score_adj);
- r = kill(pid, SIGKILL);
+ taskname, pid, uid, procp->oomadj, tasksize * page_k,
+ level_name[level], min_score_adj);
pid_remove(pid);
+ TRACE_KILL_END();
+
if (r) {
ALOGE("kill(%d): errno=%d", pid, errno);
return -1;
@@ -623,31 +743,40 @@
}
/*
- * Find a process to kill based on the current (possibly estimated) free memory
- * and cached memory sizes. Returns the size of the killed processes.
+ * Find processes to kill to free required number of pages.
+ * If pages_to_free is set to 0 only one process will be killed.
+ * Returns the size of the killed processes.
*/
-static int find_and_kill_process(bool is_critical) {
+static int find_and_kill_processes(enum vmpressure_level level,
+ int pages_to_free) {
int i;
- int killed_size = 0;
- int min_score_adj = is_critical ? critical_oomadj : medium_oomadj;
+ int killed_size;
+ int pages_freed = 0;
+ int min_score_adj = level_oomadj[level];
for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
struct proc *procp;
-retry:
- procp = proc_adj_lru(i);
+ while (true) {
+ if (is_go_device)
+ procp = proc_adj_lru(i);
+ else
+ procp = proc_get_heaviest(i);
- if (procp) {
- killed_size = kill_one_process(procp, min_score_adj, is_critical);
- if (killed_size < 0) {
- goto retry;
- } else {
- return killed_size;
+ if (!procp)
+ break;
+
+ killed_size = kill_one_process(procp, min_score_adj, level);
+ if (killed_size >= 0) {
+ pages_freed += killed_size;
+ if (pages_freed >= pages_to_free) {
+ return pages_freed;
+ }
}
}
}
- return 0;
+ return pages_freed;
}
static int64_t get_memory_usage(const char* path) {
@@ -674,33 +803,119 @@
return mem_usage;
}
-static void mp_event_common(bool is_critical) {
+void record_low_pressure_levels(struct mem_size *free_mem) {
+ if (low_pressure_mem.min_free == -1 ||
+ low_pressure_mem.min_free > free_mem->free_mem) {
+ if (debug_process_killing) {
+ ALOGI("Low pressure min memory update from %d to %d",
+ low_pressure_mem.min_free, free_mem->free_mem);
+ }
+ low_pressure_mem.min_free = free_mem->free_mem;
+ }
+ /*
+ * Free memory at low vmpressure events occasionally gets spikes,
+ * possibly a stale low vmpressure event with memory already
+ * freed up (no memory pressure should have been reported).
+ * Ignore large jumps in max_free that would mess up our stats.
+ */
+ if (low_pressure_mem.max_free == -1 ||
+ (low_pressure_mem.max_free < free_mem->free_mem &&
+ free_mem->free_mem - low_pressure_mem.max_free < low_pressure_mem.max_free * 0.1)) {
+ if (debug_process_killing) {
+ ALOGI("Low pressure max memory update from %d to %d",
+ low_pressure_mem.max_free, free_mem->free_mem);
+ }
+ low_pressure_mem.max_free = free_mem->free_mem;
+ }
+}
+
+enum vmpressure_level upgrade_level(enum vmpressure_level level) {
+ return (enum vmpressure_level)((level < VMPRESS_LEVEL_CRITICAL) ?
+ level + 1 : level);
+}
+
+enum vmpressure_level downgrade_level(enum vmpressure_level level) {
+ return (enum vmpressure_level)((level > VMPRESS_LEVEL_LOW) ?
+ level - 1 : level);
+}
+
+static inline unsigned long get_time_diff_ms(struct timeval *from,
+ struct timeval *to) {
+ return (to->tv_sec - from->tv_sec) * 1000 +
+ (to->tv_usec - from->tv_usec) / 1000;
+}
+
+static void mp_event_common(int data, uint32_t events __unused) {
int ret;
unsigned long long evcount;
- int index = is_critical ? CRITICAL_INDEX : MEDIUM_INDEX;
int64_t mem_usage, memsw_usage;
int64_t mem_pressure;
+ enum vmpressure_level lvl;
+ struct mem_size free_mem;
+ static struct timeval last_report_tm;
+ static unsigned long skip_count = 0;
+ enum vmpressure_level level = (enum vmpressure_level)data;
- ret = read(mpevfd[index], &evcount, sizeof(evcount));
- if (ret < 0)
- ALOGE("Error reading memory pressure event fd; errno=%d",
- errno);
+ /*
+ * Check all event counters from low to critical
+ * and upgrade to the highest priority one. By reading
+ * eventfd we also reset the event counters.
+ */
+ for (lvl = VMPRESS_LEVEL_LOW; lvl < VMPRESS_LEVEL_COUNT; lvl++) {
+ if (mpevfd[lvl] != -1 &&
+ read(mpevfd[lvl], &evcount, sizeof(evcount)) > 0 &&
+ evcount > 0 && lvl > level) {
+ level = lvl;
+ }
+ }
+
+ if (kill_timeout_ms) {
+ struct timeval curr_tm;
+ gettimeofday(&curr_tm, NULL);
+ if (get_time_diff_ms(&last_report_tm, &curr_tm) < kill_timeout_ms) {
+ skip_count++;
+ return;
+ }
+ }
+
+ if (skip_count > 0) {
+ if (debug_process_killing) {
+ ALOGI("%lu memory pressure events were skipped after a kill!",
+ skip_count);
+ }
+ skip_count = 0;
+ }
+
+ if (get_free_memory(&free_mem) == 0) {
+ if (level == VMPRESS_LEVEL_LOW) {
+ record_low_pressure_levels(&free_mem);
+ }
+ } else {
+ ALOGE("Failed to get free memory!");
+ return;
+ }
+
+ if (level_oomadj[level] > OOM_SCORE_ADJ_MAX) {
+ /* Do not monitor this pressure level */
+ return;
+ }
mem_usage = get_memory_usage(MEMCG_MEMORY_USAGE);
memsw_usage = get_memory_usage(MEMCG_MEMORYSW_USAGE);
if (memsw_usage < 0 || mem_usage < 0) {
- find_and_kill_process(is_critical);
- return;
+ goto do_kill;
}
// Calculate percent for swappinness.
mem_pressure = (mem_usage * 100) / memsw_usage;
- if (enable_pressure_upgrade && !is_critical) {
+ if (enable_pressure_upgrade && level != VMPRESS_LEVEL_CRITICAL) {
// We are swapping too much.
if (mem_pressure < upgrade_pressure) {
- ALOGI("Event upgraded to critical.");
- is_critical = true;
+ level = upgrade_level(level);
+ if (debug_process_killing) {
+ ALOGI("Event upgraded to %s", level_name[level]);
+ }
}
}
@@ -708,41 +923,63 @@
// kill any process, since enough memory is available.
if (mem_pressure > downgrade_pressure) {
if (debug_process_killing) {
- ALOGI("Ignore %s memory pressure", is_critical ? "critical" : "medium");
+ ALOGI("Ignore %s memory pressure", level_name[level]);
}
return;
- } else if (is_critical && mem_pressure > upgrade_pressure) {
+ } else if (level == VMPRESS_LEVEL_CRITICAL &&
+ mem_pressure > upgrade_pressure) {
if (debug_process_killing) {
ALOGI("Downgrade critical memory pressure");
}
- // Downgrade event to medium, since enough memory available.
- is_critical = false;
+ // Downgrade event, since enough memory available.
+ level = downgrade_level(level);
}
- if (find_and_kill_process(is_critical) == 0) {
- if (debug_process_killing) {
- ALOGI("Nothing to kill");
+do_kill:
+ if (is_go_device) {
+ /* For Go devices kill only one task */
+ if (find_and_kill_processes(level, 0) == 0) {
+ if (debug_process_killing) {
+ ALOGI("Nothing to kill");
+ }
+ }
+ } else {
+ /* If pressure level is less than critical and enough free swap then ignore */
+ if (level < VMPRESS_LEVEL_CRITICAL && free_mem.free_swap > low_pressure_mem.max_free) {
+ if (debug_process_killing) {
+ ALOGI("Ignoring pressure since %d swap pages are available ", free_mem.free_swap);
+ }
+ return;
+ }
+
+ /* Free up enough memory to downgrate the memory pressure to low level */
+ if (free_mem.free_mem < low_pressure_mem.max_free) {
+ int pages_to_free = low_pressure_mem.max_free - free_mem.free_mem;
+ if (debug_process_killing) {
+ ALOGI("Trying to free %d pages", pages_to_free);
+ }
+ int pages_freed = find_and_kill_processes(level, pages_to_free);
+ if (pages_freed < pages_to_free) {
+ if (debug_process_killing) {
+ ALOGI("Unable to free enough memory (pages freed=%d)",
+ pages_freed);
+ }
+ } else {
+ gettimeofday(&last_report_tm, NULL);
+ }
}
}
}
-static void mp_event(uint32_t events __unused) {
- mp_event_common(false);
-}
-
-static void mp_event_critical(uint32_t events __unused) {
- mp_event_common(true);
-}
-
-static int init_mp_common(char *levelstr, void *event_handler, bool is_critical)
-{
+static bool init_mp_common(enum vmpressure_level level) {
int mpfd;
int evfd;
int evctlfd;
char buf[256];
struct epoll_event epev;
int ret;
- int mpevfd_index = is_critical ? CRITICAL_INDEX : MEDIUM_INDEX;
+ int level_idx = (int)level;
+ const char *levelstr = level_name[level_idx];
mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC);
if (mpfd < 0) {
@@ -768,7 +1005,7 @@
goto err;
}
- ret = write(evctlfd, buf, strlen(buf) + 1);
+ ret = TEMP_FAILURE_RETRY(write(evctlfd, buf, strlen(buf) + 1));
if (ret == -1) {
ALOGE("cgroup.event_control write failed for level %s; errno=%d",
levelstr, errno);
@@ -776,15 +1013,19 @@
}
epev.events = EPOLLIN;
- epev.data.ptr = event_handler;
+ /* use data to store event level */
+ vmpressure_hinfo[level_idx].data = level_idx;
+ vmpressure_hinfo[level_idx].handler = mp_event_common;
+ epev.data.ptr = (void *)&vmpressure_hinfo[level_idx];
ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, evfd, &epev);
if (ret == -1) {
ALOGE("epoll_ctl for level %s failed; errno=%d", levelstr, errno);
goto err;
}
maxevents++;
- mpevfd[mpevfd_index] = evfd;
- return 0;
+ mpevfd[level] = evfd;
+ close(evctlfd);
+ return true;
err:
close(evfd);
@@ -793,17 +1034,7 @@
err_open_evctlfd:
close(mpfd);
err_open_mpfd:
- return -1;
-}
-
-static int init_mp_medium()
-{
- return init_mp_common(MEMPRESSURE_WATCH_MEDIUM_LEVEL, (void *)&mp_event, false);
-}
-
-static int init_mp_critical()
-{
- return init_mp_common(MEMPRESSURE_WATCH_CRITICAL_LEVEL, (void *)&mp_event_critical, true);
+ return false;
}
static int init(void) {
@@ -822,36 +1053,44 @@
return -1;
}
- ctrl_lfd = android_get_control_socket("lmkd");
- if (ctrl_lfd < 0) {
+ // mark data connections as not connected
+ for (int i = 0; i < MAX_DATA_CONN; i++) {
+ data_sock[i].sock = -1;
+ }
+
+ ctrl_sock.sock = android_get_control_socket("lmkd");
+ if (ctrl_sock.sock < 0) {
ALOGE("get lmkd control socket failed");
return -1;
}
- ret = listen(ctrl_lfd, 1);
+ ret = listen(ctrl_sock.sock, MAX_DATA_CONN);
if (ret < 0) {
ALOGE("lmkd control socket listen failed (errno=%d)", errno);
return -1;
}
epev.events = EPOLLIN;
- epev.data.ptr = (void *)ctrl_connect_handler;
- if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_lfd, &epev) == -1) {
+ ctrl_sock.handler_info.handler = ctrl_connect_handler;
+ epev.data.ptr = (void *)&(ctrl_sock.handler_info);
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_sock.sock, &epev) == -1) {
ALOGE("epoll_ctl for lmkd control socket failed (errno=%d)", errno);
return -1;
}
maxevents++;
has_inkernel_module = !access(INKERNEL_MINFREE_PATH, W_OK);
- use_inkernel_interface = has_inkernel_module && !is_go_device;
+ use_inkernel_interface = has_inkernel_module;
if (use_inkernel_interface) {
ALOGI("Using in-kernel low memory killer interface");
} else {
- ret = init_mp_medium();
- ret |= init_mp_critical();
- if (ret)
+ if (!init_mp_common(VMPRESS_LEVEL_LOW) ||
+ !init_mp_common(VMPRESS_LEVEL_MEDIUM) ||
+ !init_mp_common(VMPRESS_LEVEL_CRITICAL)) {
ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
+ return -1;
+ }
}
for (i = 0; i <= ADJTOSLOT(OOM_SCORE_ADJ_MAX); i++) {
@@ -863,12 +1102,14 @@
}
static void mainloop(void) {
+ struct event_handler_info* handler_info;
+ struct epoll_event *evt;
+
while (1) {
struct epoll_event events[maxevents];
int nevents;
int i;
- ctrl_dfd_reopened = 0;
nevents = epoll_wait(epollfd, events, maxevents, -1);
if (nevents == -1) {
@@ -878,11 +1119,33 @@
continue;
}
- for (i = 0; i < nevents; ++i) {
- if (events[i].events & EPOLLERR)
+ /*
+ * First pass to see if any data socket connections were dropped.
+ * Dropped connection should be handled before any other events
+ * to deallocate data connection and correctly handle cases when
+ * connection gets dropped and reestablished in the same epoll cycle.
+ * In such cases it's essential to handle connection closures first.
+ */
+ for (i = 0, evt = &events[0]; i < nevents; ++i, evt++) {
+ if ((evt->events & EPOLLHUP) && evt->data.ptr) {
+ ALOGI("lmkd data connection dropped");
+ handler_info = (struct event_handler_info*)evt->data.ptr;
+ ctrl_data_close(handler_info->data);
+ }
+ }
+
+ /* Second pass to handle all other events */
+ for (i = 0, evt = &events[0]; i < nevents; ++i, evt++) {
+ if (evt->events & EPOLLERR)
ALOGD("EPOLLERR on event #%d", i);
- if (events[i].data.ptr)
- (*(void (*)(uint32_t))events[i].data.ptr)(events[i].events);
+ if (evt->events & EPOLLHUP) {
+ /* This case was handled in the first pass */
+ continue;
+ }
+ if (evt->data.ptr) {
+ handler_info = (struct event_handler_info*)evt->data.ptr;
+ handler_info->handler(handler_info->data, evt->events);
+ }
}
}
}
@@ -892,13 +1155,27 @@
.sched_priority = 1,
};
- medium_oomadj = property_get_int32("ro.lmk.medium", 800);
- critical_oomadj = property_get_int32("ro.lmk.critical", 0);
+ /* By default disable low level vmpressure events */
+ level_oomadj[VMPRESS_LEVEL_LOW] =
+ property_get_int32("ro.lmk.low", OOM_SCORE_ADJ_MAX + 1);
+ level_oomadj[VMPRESS_LEVEL_MEDIUM] =
+ property_get_int32("ro.lmk.medium", 800);
+ level_oomadj[VMPRESS_LEVEL_CRITICAL] =
+ property_get_int32("ro.lmk.critical", 0);
debug_process_killing = property_get_bool("ro.lmk.debug", false);
- enable_pressure_upgrade = property_get_bool("ro.lmk.critical_upgrade", false);
- upgrade_pressure = (int64_t)property_get_int32("ro.lmk.upgrade_pressure", 50);
- downgrade_pressure = (int64_t)property_get_int32("ro.lmk.downgrade_pressure", 60);
+
+ /* By default disable upgrade/downgrade logic */
+ enable_pressure_upgrade =
+ property_get_bool("ro.lmk.critical_upgrade", false);
+ upgrade_pressure =
+ (int64_t)property_get_int32("ro.lmk.upgrade_pressure", 100);
+ downgrade_pressure =
+ (int64_t)property_get_int32("ro.lmk.downgrade_pressure", 100);
+ kill_heaviest_task =
+ property_get_bool("ro.lmk.kill_heaviest_task", true);
is_go_device = property_get_bool("ro.config.low_ram", false);
+ kill_timeout_ms =
+ (unsigned long)property_get_int32("ro.lmk.kill_timeout_ms", 0);
// MCL_ONFAULT pins pages as they fault instead of loading
// everything immediately all at once. (Which would be bad,
diff --git a/lmkd/tests/Android.bp b/lmkd/tests/Android.bp
new file mode 100644
index 0000000..cbf44e9
--- /dev/null
+++ b/lmkd/tests/Android.bp
@@ -0,0 +1,40 @@
+// 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.
+
+cc_test {
+ name: "lmkd_unit_test",
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ static_libs: [
+ "liblmkd_utils",
+ ],
+
+ target: {
+ android: {
+ srcs: ["lmkd_test.cpp"],
+ },
+ },
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+
+ compile_multilib: "first",
+}
diff --git a/lmkd/tests/lmkd_test.cpp b/lmkd/tests/lmkd_test.cpp
new file mode 100644
index 0000000..f17512d
--- /dev/null
+++ b/lmkd/tests/lmkd_test.cpp
@@ -0,0 +1,368 @@
+/*
+ * 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 <sstream>
+#include <stdio.h>
+#include <string.h>
+#include <string>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+#include <lmkd.h>
+#include <liblmkd_utils.h>
+#include <log/log_properties.h>
+#include <private/android_filesystem_config.h>
+
+using namespace android::base;
+
+#define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
+#define LMKDTEST_RESPAWN_FLAG "LMKDTEST_RESPAWN"
+
+#define LMKD_LOGCAT_MARKER "lowmemorykiller"
+#define LMKD_KILL_MARKER_TEMPLATE LMKD_LOGCAT_MARKER ": Killing '%s'"
+#define OOM_MARKER "Out of memory"
+#define OOM_KILL_MARKER "Killed process"
+#define MIN_LOG_SIZE 100
+
+#define ONE_MB (1 << 20)
+
+/* Test constant parameters */
+#define OOM_ADJ_MAX 1000
+#define OOM_ADJ_MIN 0
+#define OOM_ADJ_STEP 100
+#define STEP_COUNT ((OOM_ADJ_MAX - OOM_ADJ_MIN) / OOM_ADJ_STEP + 1)
+
+#define ALLOC_STEP (ONE_MB)
+#define ALLOC_DELAY 1000
+
+/* Utility functions */
+std::string readCommand(const std::string& command) {
+ FILE* fp = popen(command.c_str(), "r");
+ std::string content;
+ ReadFdToString(fileno(fp), &content);
+ pclose(fp);
+ return content;
+}
+
+std::string readLogcat(const std::string& marker) {
+ std::string content = readCommand("logcat -d -b all");
+ size_t pos = content.find(marker);
+ if (pos == std::string::npos) return "";
+ content.erase(0, pos);
+ return content;
+}
+
+bool writeFile(const std::string& file, const std::string& string) {
+ if (getuid() == static_cast<unsigned>(AID_ROOT)) {
+ return WriteStringToFile(string, file);
+ }
+ return string == readCommand(
+ "echo -n '" + string + "' | su root tee " + file + " 2>&1");
+}
+
+bool writeKmsg(const std::string& marker) {
+ return writeFile("/dev/kmsg", marker);
+}
+
+std::string getTextAround(const std::string& text, size_t pos,
+ size_t lines_before, size_t lines_after) {
+ size_t start_pos = pos;
+
+ // find start position
+ // move up lines_before number of lines
+ while (lines_before > 0 &&
+ (start_pos = text.rfind('\n', start_pos)) != std::string::npos) {
+ lines_before--;
+ }
+ // move to the beginning of the line
+ start_pos = text.rfind('\n', start_pos);
+ start_pos = (start_pos == std::string::npos) ? 0 : start_pos + 1;
+
+ // find end position
+ // move down lines_after number of lines
+ while (lines_after > 0 &&
+ (pos = text.find('\n', pos)) != std::string::npos) {
+ pos++;
+ lines_after--;
+ }
+ return text.substr(start_pos, (pos == std::string::npos) ?
+ std::string::npos : pos - start_pos);
+}
+
+bool getExecPath(std::string &path) {
+ // exec path as utf8z c_str().
+ // std::string contains _all_ nul terminated argv[] strings.
+ return android::base::ReadFileToString("/proc/self/cmdline", &path);
+}
+
+/* Child synchronization primitives */
+#define STATE_INIT 0
+#define STATE_CHILD_READY 1
+#define STATE_PARENT_READY 2
+
+struct state_sync {
+ pthread_mutex_t mutex;
+ pthread_cond_t condition;
+ int state;
+};
+
+struct state_sync * init_state_sync_obj() {
+ struct state_sync *ssync;
+
+ ssync = (struct state_sync*)mmap(NULL, sizeof(struct state_sync),
+ PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+ if (ssync == MAP_FAILED) {
+ return NULL;
+ }
+
+ pthread_mutexattr_t mattr;
+ pthread_mutexattr_init(&mattr);
+ pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
+ pthread_mutex_init(&ssync->mutex, &mattr);
+
+ pthread_condattr_t cattr;
+ pthread_condattr_init(&cattr);
+ pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
+ pthread_cond_init(&ssync->condition, &cattr);
+
+ ssync->state = STATE_INIT;
+ return ssync;
+}
+
+void destroy_state_sync_obj(struct state_sync *ssync) {
+ pthread_cond_destroy(&ssync->condition);
+ pthread_mutex_destroy(&ssync->mutex);
+ munmap(ssync, sizeof(struct state_sync));
+}
+
+void signal_state(struct state_sync *ssync, int state) {
+ pthread_mutex_lock(&ssync->mutex);
+ ssync->state = state;
+ pthread_cond_signal(&ssync->condition);
+ pthread_mutex_unlock(&ssync->mutex);
+}
+
+void wait_for_state(struct state_sync *ssync, int state) {
+ pthread_mutex_lock(&ssync->mutex);
+ while (ssync->state != state) {
+ pthread_cond_wait(&ssync->condition, &ssync->mutex);
+ }
+ pthread_mutex_unlock(&ssync->mutex);
+}
+
+/* Memory allocation and data sharing */
+struct shared_data {
+ size_t allocated;
+ bool finished;
+ size_t total_size;
+ size_t step_size;
+ size_t step_delay;
+ int oomadj;
+};
+
+volatile void *gptr;
+void add_pressure(struct shared_data *data) {
+ volatile void *ptr;
+ size_t allocated_size = 0;
+
+ data->finished = false;
+ while (allocated_size < data->total_size) {
+ ptr = mmap(NULL, data->step_size, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
+ if (ptr != MAP_FAILED) {
+ /* create ptr aliasing to prevent compiler optimizing the access */
+ gptr = ptr;
+ /* make data non-zero */
+ memset((void*)ptr, (int)(allocated_size + 1), data->step_size);
+ allocated_size += data->step_size;
+ data->allocated = allocated_size;
+ }
+ usleep(data->step_delay);
+ }
+ data->finished = (allocated_size >= data->total_size);
+}
+
+/* Memory stress test main body */
+void runMemStressTest() {
+ struct shared_data *data;
+ struct state_sync *ssync;
+ int sock;
+ pid_t pid;
+ uid_t uid = getuid();
+
+ ASSERT_FALSE((sock = lmkd_connect()) < 0)
+ << "Failed to connect to lmkd process, err=" << strerror(errno);
+
+ /* allocate shared memory to communicate params with a child */
+ data = (struct shared_data*)mmap(NULL, sizeof(struct shared_data),
+ PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+ ASSERT_FALSE(data == MAP_FAILED) << "Memory allocation failure";
+ data->total_size = (size_t)-1; /* allocate until killed */
+ data->step_size = ALLOC_STEP;
+ data->step_delay = ALLOC_DELAY;
+
+ /* allocate state sync object */
+ ASSERT_FALSE((ssync = init_state_sync_obj()) == NULL)
+ << "Memory allocation failure";
+
+ /* run the test gradually decreasing oomadj */
+ data->oomadj = OOM_ADJ_MAX;
+ while (data->oomadj >= OOM_ADJ_MIN) {
+ ASSERT_FALSE((pid = fork()) < 0)
+ << "Failed to spawn a child process, err=" << strerror(errno);
+ if (pid != 0) {
+ /* Parent */
+ struct lmk_procprio params;
+ /* wait for child to start and get ready */
+ wait_for_state(ssync, STATE_CHILD_READY);
+ params.pid = pid;
+ params.uid = uid;
+ params.oomadj = data->oomadj;
+ ASSERT_FALSE(lmkd_register_proc(sock, ¶ms) < 0)
+ << "Failed to communicate with lmkd, err=" << strerror(errno);
+ // signal the child it can proceed
+ signal_state(ssync, STATE_PARENT_READY);
+ waitpid(pid, NULL, 0);
+ if (data->finished) {
+ GTEST_LOG_(INFO) << "Child [pid=" << pid << "] allocated "
+ << data->allocated / ONE_MB << "MB";
+ } else {
+ GTEST_LOG_(INFO) << "Child [pid=" << pid << "] allocated "
+ << data->allocated / ONE_MB
+ << "MB before being killed";
+ }
+ data->oomadj -= OOM_ADJ_STEP;
+ } else {
+ /* Child */
+ pid = getpid();
+ GTEST_LOG_(INFO) << "Child [pid=" << pid
+ << "] is running at oomadj="
+ << data->oomadj;
+ data->allocated = 0;
+ data->finished = false;
+ ASSERT_FALSE(create_memcg(uid, pid) != 0)
+ << "Child [pid=" << pid << "] failed to create a cgroup";
+ signal_state(ssync, STATE_CHILD_READY);
+ wait_for_state(ssync, STATE_PARENT_READY);
+ add_pressure(data);
+ /* should not reach here, child should be killed by OOM/LMK */
+ FAIL() << "Child [pid=" << pid << "] was not killed";
+ break;
+ }
+ }
+ destroy_state_sync_obj(ssync);
+ munmap(data, sizeof(struct shared_data));
+ close(sock);
+}
+
+TEST(lmkd, check_for_oom) {
+ // test requirements
+ // userdebug build
+ if (!__android_log_is_debuggable()) {
+ GTEST_LOG_(INFO) << "Must be userdebug build, terminating test";
+ return;
+ }
+ // check if in-kernel LMK driver is present
+ if (!access(INKERNEL_MINFREE_PATH, W_OK)) {
+ GTEST_LOG_(INFO) << "Must not have kernel lowmemorykiller driver,"
+ << " terminating test";
+ return;
+ }
+
+ // if respawned test process then run the test and exit (no analysis)
+ if (getenv(LMKDTEST_RESPAWN_FLAG) != NULL) {
+ runMemStressTest();
+ return;
+ }
+
+ // Main test process
+ // mark the beginning of the test
+ std::string marker = StringPrintf(
+ "LMKD test start %lu\n", static_cast<unsigned long>(time(nullptr)));
+ ASSERT_TRUE(writeKmsg(marker));
+
+ // get executable complete path
+ std::string test_path;
+ ASSERT_TRUE(getExecPath(test_path));
+
+ std::string test_output;
+ if (getuid() != static_cast<unsigned>(AID_ROOT)) {
+ // if not root respawn itself as root and capture output
+ std::string command = StringPrintf(
+ "%s=true su root %s --gtest_filter=lmkd.check_for_oom 2>&1",
+ LMKDTEST_RESPAWN_FLAG, test_path.c_str());
+ std::string test_output = readCommand(command);
+ GTEST_LOG_(INFO) << test_output;
+ } else {
+ // main test process is root, run the test
+ runMemStressTest();
+ }
+
+ // Analyze results
+ // capture logcat containind kernel logs
+ std::string logcat_out = readLogcat(marker);
+
+ // 1. extract LMKD kills from logcat output, count kills
+ std::stringstream kill_logs;
+ int hit_count = 0;
+ size_t pos = 0;
+ marker = StringPrintf(LMKD_KILL_MARKER_TEMPLATE, test_path.c_str());
+
+ while (true) {
+ if ((pos = logcat_out.find(marker, pos)) != std::string::npos) {
+ kill_logs << getTextAround(logcat_out, pos, 0, 1);
+ pos += marker.length();
+ hit_count++;
+ } else {
+ break;
+ }
+ }
+ GTEST_LOG_(INFO) << "====Logged kills====" << std::endl
+ << kill_logs.str();
+ EXPECT_TRUE(hit_count == STEP_COUNT) << "Number of kills " << hit_count
+ << " is less than expected "
+ << STEP_COUNT;
+
+ // 2. check kernel logs for OOM kills
+ pos = logcat_out.find(OOM_MARKER);
+ bool oom_detected = (pos != std::string::npos);
+ bool oom_kill_detected = (oom_detected &&
+ logcat_out.find(OOM_KILL_MARKER, pos) != std::string::npos);
+
+ EXPECT_FALSE(oom_kill_detected) << "OOM kill is detected!";
+ if (oom_detected || oom_kill_detected) {
+ // capture logcat with logs around all OOMs
+ pos = 0;
+ while ((pos = logcat_out.find(OOM_MARKER, pos)) != std::string::npos) {
+ GTEST_LOG_(INFO) << "====Logs around OOM====" << std::endl
+ << getTextAround(logcat_out, pos,
+ MIN_LOG_SIZE / 2, MIN_LOG_SIZE / 2);
+ pos += strlen(OOM_MARKER);
+ }
+ }
+
+ // output complete logcat with kernel (might get truncated)
+ GTEST_LOG_(INFO) << "====Complete logcat output====" << std::endl
+ << logcat_out;
+}
+
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 560f490..a78319f 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -171,7 +171,9 @@
}
// audit message (except sequence number) identical?
- if (last->isBinary()) {
+ if (last->isBinary() &&
+ (lenl > static_cast<ssize_t>(sizeof(android_log_event_string_t))) &&
+ (lenr > static_cast<ssize_t>(sizeof(android_log_event_string_t)))) {
if (fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_string_t) -
sizeof(int32_t))) {
return DIFFERENT;
@@ -198,7 +200,7 @@
int LogBuffer::log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid,
pid_t tid, const char* msg, unsigned short len) {
- if ((log_id >= LOG_ID_MAX) || (log_id < 0)) {
+ if (log_id >= LOG_ID_MAX) {
return -EINVAL;
}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index feb100e..f488ed5 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -93,6 +93,23 @@
else
LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product $(TARGET_ROOT_OUT)/product
endif
+
+# For /odm partition.
+LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm
+# For Treble Generic System Image (GSI), system-as-root GSI needs to work on
+# both devices with and without /odm partition. Those symlinks are for devices
+# without /odm partition. For devices with /odm partition, mount odm.img under
+# /odm will hide those symlinks.
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/app $(TARGET_ROOT_OUT)/odm/app
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/bin $(TARGET_ROOT_OUT)/odm/bin
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/etc $(TARGET_ROOT_OUT)/odm/etc
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/firmware $(TARGET_ROOT_OUT)/odm/firmware
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/framework $(TARGET_ROOT_OUT)/odm/framework
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/lib $(TARGET_ROOT_OUT)/odm/lib
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/lib64 $(TARGET_ROOT_OUT)/odm/lib64
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/overlay $(TARGET_ROOT_OUT)/odm/overlay
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/priv-app $(TARGET_ROOT_OUT)/odm/priv-app
+
ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
else
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index c8d87c8..6e46295 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -54,6 +54,9 @@
namespace.default.permitted.paths += /vendor/framework
namespace.default.permitted.paths += /vendor/app
namespace.default.permitted.paths += /vendor/priv-app
+namespace.default.permitted.paths += /odm/framework
+namespace.default.permitted.paths += /odm/app
+namespace.default.permitted.paths += /odm/priv-app
namespace.default.permitted.paths += /oem/app
namespace.default.permitted.paths += /product/framework
namespace.default.permitted.paths += /product/app
@@ -74,6 +77,9 @@
namespace.default.asan.permitted.paths += /vendor/framework
namespace.default.asan.permitted.paths += /vendor/app
namespace.default.asan.permitted.paths += /vendor/priv-app
+namespace.default.asan.permitted.paths += /odm/framework
+namespace.default.asan.permitted.paths += /odm/app
+namespace.default.asan.permitted.paths += /odm/priv-app
namespace.default.asan.permitted.paths += /oem/app
namespace.default.asan.permitted.paths += /product/framework
namespace.default.asan.permitted.paths += /product/app