Merge "storaged: record IO perf history from proto updates"
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 8c24bbb..6b30be8 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -257,19 +257,6 @@
send_packet(cp, t);
}
-#if ADB_HOST
-
-void SendConnectOnHost(atransport* t) {
- // Send an empty message before A_CNXN message. This is because the data toggle of the ep_out on
- // host and ep_in on device may not be the same.
- apacket* p = get_apacket();
- CHECK(p);
- send_packet(p, t);
- send_connect(t);
-}
-
-#endif
-
// qual_overwrite is used to overwrite a qualifier string. dst is a
// pointer to a char pointer. It is assumed that if *dst is non-NULL, it
// was malloc'ed and needs to freed. *dst will be set to a dup of src.
@@ -370,7 +357,7 @@
if (p->msg.arg0){
send_packet(p, t);
#if ADB_HOST
- SendConnectOnHost(t);
+ send_connect(t);
#endif
} else {
t->SetConnectionState(kCsOffline);
diff --git a/adb/adb.h b/adb/adb.h
index 6a9897f..a4d233e 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -224,9 +224,6 @@
void handle_offline(atransport *t);
void send_connect(atransport *t);
-#if ADB_HOST
-void SendConnectOnHost(atransport* t);
-#endif
void parse_banner(const std::string&, atransport* t);
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 7025f28..8120199 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -333,6 +333,13 @@
return;
}
+ rc = libusb_set_interface_alt_setting(handle.get(), interface_num, 0);
+ if (rc != 0) {
+ LOG(WARNING) << "failed to set interface alt setting for device '" << device_serial
+ << "'" << libusb_error_name(rc);
+ return;
+ }
+
for (uint8_t endpoint : {bulk_in, bulk_out}) {
rc = libusb_clear_halt(handle.get(), endpoint);
if (rc != 0) {
diff --git a/adb/services.cpp b/adb/services.cpp
index ca34556..aff7012 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -58,6 +58,7 @@
#include "transport.h"
struct stinfo {
+ const char* service_name;
void (*func)(int fd, void *cookie);
int fd;
void *cookie;
@@ -65,7 +66,7 @@
static void service_bootstrap_func(void* x) {
stinfo* sti = reinterpret_cast<stinfo*>(x);
- adb_thread_setname(android::base::StringPrintf("service %d", sti->fd));
+ adb_thread_setname(android::base::StringPrintf("%s svc %d", sti->service_name, sti->fd));
sti->func(sti->fd, sti->cookie);
free(sti);
}
@@ -160,8 +161,7 @@
return true;
}
-void reboot_service(int fd, void* arg)
-{
+void reboot_service(int fd, void* arg) {
if (reboot_service_impl(fd, static_cast<const char*>(arg))) {
// Don't return early. Give the reboot command time to take effect
// to avoid messing up scripts which do "adb reboot && adb wait-for-device"
@@ -236,8 +236,7 @@
#endif // !ADB_HOST
-static int create_service_thread(void (*func)(int, void *), void *cookie)
-{
+static int create_service_thread(const char* service_name, void (*func)(int, void*), void* cookie) {
int s[2];
if (adb_socketpair(s)) {
printf("cannot create service socket pair\n");
@@ -258,6 +257,7 @@
if (sti == nullptr) {
fatal("cannot allocate stinfo");
}
+ sti->service_name = service_name;
sti->func = func;
sti->cookie = cookie;
sti->fd = s[1];
@@ -281,7 +281,7 @@
} else if(!strncmp("dev:", name, 4)) {
ret = unix_open(name + 4, O_RDWR | O_CLOEXEC);
} else if(!strncmp(name, "framebuffer:", 12)) {
- ret = create_service_thread(framebuffer_service, 0);
+ ret = create_service_thread("fb", framebuffer_service, nullptr);
} else if (!strncmp(name, "jdwp:", 5)) {
ret = create_jdwp_connection_fd(atoi(name+5));
} else if(!strncmp(name, "shell", 5)) {
@@ -289,17 +289,17 @@
} else if(!strncmp(name, "exec:", 5)) {
ret = StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
} else if(!strncmp(name, "sync:", 5)) {
- ret = create_service_thread(file_sync_service, NULL);
+ ret = create_service_thread("sync", file_sync_service, nullptr);
} else if(!strncmp(name, "remount:", 8)) {
- ret = create_service_thread(remount_service, NULL);
+ ret = create_service_thread("remount", remount_service, nullptr);
} else if(!strncmp(name, "reboot:", 7)) {
void* arg = strdup(name + 7);
if (arg == NULL) return -1;
- ret = create_service_thread(reboot_service, arg);
+ ret = create_service_thread("reboot", reboot_service, arg);
} else if(!strncmp(name, "root:", 5)) {
- ret = create_service_thread(restart_root_service, NULL);
+ ret = create_service_thread("root", restart_root_service, nullptr);
} else if(!strncmp(name, "unroot:", 7)) {
- ret = create_service_thread(restart_unroot_service, NULL);
+ ret = create_service_thread("unroot", restart_unroot_service, nullptr);
} else if(!strncmp(name, "backup:", 7)) {
ret = StartSubprocess(android::base::StringPrintf("/system/bin/bu backup %s",
(name + 7)).c_str(),
@@ -312,17 +312,20 @@
if (sscanf(name + 6, "%d", &port) != 1) {
return -1;
}
- ret = create_service_thread(restart_tcp_service, (void *) (uintptr_t) port);
+ ret = create_service_thread("tcp", restart_tcp_service, reinterpret_cast<void*>(port));
} else if(!strncmp(name, "usb:", 4)) {
- ret = create_service_thread(restart_usb_service, NULL);
+ ret = create_service_thread("usb", restart_usb_service, nullptr);
} else if (!strncmp(name, "reverse:", 8)) {
ret = reverse_service(name + 8);
} else if(!strncmp(name, "disable-verity:", 15)) {
- ret = create_service_thread(set_verity_enabled_state_service, (void*)0);
+ ret = create_service_thread("verity-on", set_verity_enabled_state_service,
+ reinterpret_cast<void*>(0));
} else if(!strncmp(name, "enable-verity:", 15)) {
- ret = create_service_thread(set_verity_enabled_state_service, (void*)1);
+ ret = create_service_thread("verity-off", set_verity_enabled_state_service,
+ reinterpret_cast<void*>(1));
} else if (!strcmp(name, "reconnect")) {
- ret = create_service_thread(reconnect_service, const_cast<atransport*>(transport));
+ ret = create_service_thread("reconnect", reconnect_service,
+ const_cast<atransport*>(transport));
#endif
}
if (ret >= 0) {
@@ -484,14 +487,14 @@
return nullptr;
}
- int fd = create_service_thread(wait_for_state, sinfo.get());
+ int fd = create_service_thread("wait", wait_for_state, sinfo.get());
if (fd != -1) {
sinfo.release();
}
return create_local_socket(fd);
} else if (!strncmp(name, "connect:", 8)) {
char* host = strdup(name + 8);
- int fd = create_service_thread(connect_service, host);
+ int fd = create_service_thread("connect", connect_service, host);
if (fd == -1) {
free(host);
}
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index ee821f8..0c7e1f9 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -95,6 +95,7 @@
#include <vector>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <private/android_logger.h>
@@ -212,6 +213,13 @@
WaitForExit();
}
+static std::string GetHostName() {
+ char buf[HOST_NAME_MAX];
+ if (gethostname(buf, sizeof(buf)) != -1 && strcmp(buf, "localhost") != 0) return buf;
+
+ return android::base::GetProperty("ro.product.device", "android");
+}
+
bool Subprocess::ForkAndExec(std::string* error) {
unique_fd child_stdinout_sfd, child_stderr_sfd;
unique_fd parent_error_sfd, child_error_sfd;
@@ -250,11 +258,11 @@
}
if (pw != nullptr) {
- // TODO: $HOSTNAME? Normally bash automatically sets that, but mksh doesn't.
env["HOME"] = pw->pw_dir;
+ env["HOSTNAME"] = GetHostName();
env["LOGNAME"] = pw->pw_name;
- env["USER"] = pw->pw_name;
env["SHELL"] = pw->pw_shell;
+ env["USER"] = pw->pw_name;
}
if (!terminal_type_.empty()) {
@@ -435,8 +443,7 @@
void Subprocess::ThreadHandler(void* userdata) {
Subprocess* subprocess = reinterpret_cast<Subprocess*>(userdata);
- adb_thread_setname(android::base::StringPrintf(
- "shell srvc %d", subprocess->pid()));
+ adb_thread_setname(android::base::StringPrintf("shell svc %d", subprocess->pid()));
D("passing data streams for PID %d", subprocess->pid());
subprocess->PassDataStreams();
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 34cc026..089a1ec 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -1101,11 +1101,4 @@
keys_.pop_front();
return result;
}
-bool atransport::SetSendConnectOnError() {
- if (has_send_connect_on_error_) {
- return false;
- }
- has_send_connect_on_error_ = true;
- return true;
-}
#endif
diff --git a/adb/transport.h b/adb/transport.h
index 79e3075..8c101fd 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -122,7 +122,6 @@
#if ADB_HOST
std::shared_ptr<RSA> NextKey();
- bool SetSendConnectOnError();
#endif
char token[TOKEN_SIZE] = {};
@@ -181,7 +180,6 @@
std::atomic<ConnectionState> connection_state_;
#if ADB_HOST
std::deque<std::shared_ptr<RSA>> keys_;
- bool has_send_connect_on_error_ = false;
#endif
DISALLOW_COPY_AND_ASSIGN(atransport);
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 6768d31..fdecccf 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -103,13 +103,6 @@
err_msg:
p->msg.command = 0;
- if (t->GetConnectionState() == kCsOffline) {
- // If the data toggle of ep_out on device and ep_in on host are not the same, we may receive
- // an error message. In this case, resend one A_CNXN message to connect the device.
- if (t->SetSendConnectOnError()) {
- SendConnectOnHost(t);
- }
- }
return 0;
}
@@ -162,8 +155,7 @@
return 0;
}
-static void remote_close(atransport *t)
-{
+static void remote_close(atransport* t) {
usb_close(t->usb);
t->usb = 0;
}
diff --git a/base/Android.bp b/base/Android.bp
index 82aee2a..0fd00ea 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -89,13 +89,12 @@
cppflags: ["-Wexit-time-destructors"],
enabled: true,
},
- linux: {
+ linux_glibc: {
srcs: [
"chrono_utils.cpp",
"errors_unix.cpp",
],
cppflags: ["-Wexit-time-destructors"],
- host_ldlibs: ["-lrt"],
},
windows: {
srcs: [
@@ -136,9 +135,8 @@
misc_undefined: ["integer"],
},
},
- linux: {
+ linux_glibc: {
srcs: ["chrono_utils_test.cpp"],
- host_ldlibs: ["-lrt"],
},
windows: {
srcs: ["utf8_test.cpp"],
diff --git a/base/chrono_utils.cpp b/base/chrono_utils.cpp
index b6bf701..dbe5483 100644
--- a/base/chrono_utils.cpp
+++ b/base/chrono_utils.cpp
@@ -22,7 +22,7 @@
namespace base {
boot_clock::time_point boot_clock::now() {
-#ifdef __ANDROID__
+#ifdef __linux__
timespec ts;
clock_gettime(CLOCK_BOOTTIME, &ts);
return boot_clock::time_point(std::chrono::seconds(ts.tv_sec) +
@@ -30,7 +30,7 @@
#else
// Darwin does not support clock_gettime.
return boot_clock::time_point();
-#endif // __ANDROID__
+#endif // __linux__
}
std::ostream& operator<<(std::ostream& os, const Timer& t) {
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 17986b9..d26cf85 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -570,7 +570,7 @@
ret = "reboot";
if (android::base::StartsWith(reason, "reboot")) {
reason = reason.substr(strlen("reboot"));
- while (reason[0] == ',') {
+ while ((reason[0] == ',') || (reason[0] == '_')) {
reason = reason.substr(1);
}
}
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 9f52fdf..92c6ee8 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -788,47 +788,21 @@
}
/*
- * Returns the 1st matching fstab_rec that follows the start_rec.
- * start_rec is the result of a previous search or NULL.
+ * Returns the fstab_rec* whose mount_point is path.
+ * Returns nullptr if not found.
*/
-struct fstab_rec* fs_mgr_get_entry_for_mount_point_after(struct fstab_rec* start_rec,
- struct fstab* fstab,
- const std::string& path) {
- int i;
+struct fstab_rec* fs_mgr_get_entry_for_mount_point(struct fstab* fstab, const std::string& path) {
if (!fstab) {
return nullptr;
}
-
- if (start_rec) {
- for (i = 0; i < fstab->num_entries; i++) {
- if (&fstab->recs[i] == start_rec) {
- i++;
- break;
- }
- }
- } else {
- i = 0;
- }
-
- for (; i < fstab->num_entries; i++) {
+ for (int i = 0; i < fstab->num_entries; i++) {
if (fstab->recs[i].mount_point && path == fstab->recs[i].mount_point) {
return &fstab->recs[i];
}
}
-
return nullptr;
}
-/*
- * Returns the 1st matching mount point.
- * There might be more. To look for others, use fs_mgr_get_entry_for_mount_point_after()
- * and give the fstab_rec from the previous search.
- */
-struct fstab_rec *fs_mgr_get_entry_for_mount_point(struct fstab *fstab, const char *path)
-{
- return fs_mgr_get_entry_for_mount_point_after(NULL, fstab, path);
-}
-
int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
{
return fstab->fs_mgr_flags & MF_VOLDMANAGED;
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 15c8caf..0a8496b 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -71,7 +71,6 @@
int fs_mgr_add_entry(struct fstab* fstab, const char* mount_point, const char* fs_type,
const char* blk_device);
-struct fstab_rec* fs_mgr_get_entry_for_mount_point(struct fstab* fstab, const char* path);
int fs_mgr_is_voldmanaged(const struct fstab_rec* fstab);
int fs_mgr_is_nonremovable(const struct fstab_rec* fstab);
int fs_mgr_is_verified(const struct fstab_rec* fstab);
@@ -96,6 +95,7 @@
// TODO: move this into separate header files under include/fs_mgr/*.h
#ifdef __cplusplus
std::string fs_mgr_get_slot_suffix();
+struct fstab_rec* fs_mgr_get_entry_for_mount_point(struct fstab* fstab, const std::string& path);
#endif
#endif /* __CORE_FS_TAB_H */
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 56f5148..d348866 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -5,3 +5,72 @@
header_libs: ["libbatteryservice_headers"],
export_header_lib_headers: ["libbatteryservice_headers"],
}
+
+cc_library_static {
+ name: "libbatterymonitor",
+ srcs: ["BatteryMonitor.cpp"],
+ vendor_available: true,
+ export_include_dirs: ["include"],
+ shared_libs: [
+ "libutils",
+ "libbase",
+ ],
+ header_libs: ["libhealthd_headers"],
+ export_header_lib_headers: ["libhealthd_headers"],
+}
+
+cc_library_static {
+ name: "android.hardware.health@2.0-impl",
+ vendor_available: true,
+ srcs: [
+ "Health.cpp",
+ "healthd_common.cpp",
+ ],
+
+ cflags: ["-DHEALTHD_USE_HEALTH_2_0"],
+
+ export_include_dirs: ["include"],
+
+ shared_libs: [
+ "libbase",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "liblog",
+ "libutils",
+ "libcutils",
+ "android.hardware.health@2.0",
+ ],
+
+ static_libs: [
+ "libbatterymonitor",
+ "android.hardware.health@1.0-convert",
+ ],
+}
+
+cc_binary {
+ name: "android.hardware.health@2.0-service",
+ init_rc: ["android.hardware.health@2.0-service.rc"],
+ vendor: true,
+ relative_install_path: "hw",
+ srcs: ["HealthService.cpp"],
+
+ cflags: ["-DHEALTH_INSTANCE_NAME=\"default\""],
+
+ static_libs: [
+ "android.hardware.health@2.0-impl",
+ "android.hardware.health@1.0-convert",
+ "libbatterymonitor",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libhidlbase",
+ "libhidltransport",
+ "libhwbinder",
+ "liblog",
+ "libutils",
+ "android.hardware.health@2.0",
+ ],
+}
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 6b14289..8c3dcfd 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -3,14 +3,6 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := BatteryMonitor.cpp
-LOCAL_MODULE := libbatterymonitor
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_STATIC_LIBRARIES := libutils libbase libbinder
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
healthd_mode_android.cpp \
BatteryPropertiesRegistrar.cpp
diff --git a/healthd/Health.cpp b/healthd/Health.cpp
new file mode 100644
index 0000000..74f3eec
--- /dev/null
+++ b/healthd/Health.cpp
@@ -0,0 +1,163 @@
+#define LOG_TAG "Health"
+#include <android-base/logging.h>
+
+#include <health2/Health.h>
+
+#include <hidl/HidlTransportSupport.h>
+
+extern void healthd_battery_update_internal(bool);
+
+namespace android {
+namespace hardware {
+namespace health {
+namespace V2_0 {
+namespace implementation {
+
+Health::Health(struct healthd_config* c) {
+ battery_monitor_ = std::make_unique<BatteryMonitor>();
+ battery_monitor_->init(c);
+}
+
+// Methods from IHealth follow.
+Return<Result> Health::registerCallback(const sp<IHealthInfoCallback>& callback) {
+ if (callback == nullptr) {
+ return Result::SUCCESS;
+ }
+
+ {
+ std::lock_guard<std::mutex> _lock(callbacks_lock_);
+ callbacks_.push_back(callback);
+ // unlock
+ }
+
+ auto linkRet = callback->linkToDeath(this, 0u /* cookie */);
+ if (!linkRet.withDefault(false)) {
+ LOG(WARNING) << __func__ << "Cannot link to death: "
+ << (linkRet.isOk() ? "linkToDeath returns false" : linkRet.description());
+ // ignore the error
+ }
+
+ return update();
+}
+
+bool Health::unregisterCallbackInternal(const sp<IBase>& callback) {
+ if (callback == nullptr) return false;
+
+ bool removed = false;
+ std::lock_guard<std::mutex> _lock(callbacks_lock_);
+ for (auto it = callbacks_.begin(); it != callbacks_.end();) {
+ if (interfacesEqual(*it, callback)) {
+ it = callbacks_.erase(it);
+ removed = true;
+ } else {
+ ++it;
+ }
+ }
+ (void)callback->unlinkToDeath(this).isOk(); // ignore errors
+ return removed;
+}
+
+Return<Result> Health::unregisterCallback(const sp<IHealthInfoCallback>& callback) {
+ return unregisterCallbackInternal(callback) ? Result::SUCCESS : Result::NOT_FOUND;
+}
+
+template<typename T>
+void getProperty(const std::unique_ptr<BatteryMonitor>& monitor, int id, T defaultValue,
+ const std::function<void(Result, T)>& callback) {
+ struct BatteryProperty prop;
+ T ret = defaultValue;
+ Result result = Result::SUCCESS;
+ status_t err = monitor->getProperty(static_cast<int>(id), &prop);
+ if (err != OK) {
+ LOG(DEBUG) << "getProperty(" << id << ")" << " fails: (" << err << ") " << strerror(-err);
+ } else {
+ ret = static_cast<T>(prop.valueInt64);
+ }
+ switch (err) {
+ case OK: result = Result::SUCCESS; break;
+ case NAME_NOT_FOUND: result = Result::NOT_SUPPORTED; break;
+ default: result = Result::UNKNOWN; break;
+ }
+ callback(result, static_cast<T>(ret));
+}
+
+Return<void> Health::getChargeCounter(getChargeCounter_cb _hidl_cb) {
+ getProperty(battery_monitor_, BATTERY_PROP_CHARGE_COUNTER, INT32_MIN, _hidl_cb);
+ return Void();
+}
+
+Return<void> Health::getCurrentNow(getCurrentNow_cb _hidl_cb) {
+ getProperty(battery_monitor_, BATTERY_PROP_CURRENT_NOW, INT32_MIN, _hidl_cb);
+ return Void();
+}
+
+Return<void> Health::getCurrentAverage(getCurrentAverage_cb _hidl_cb) {
+ getProperty(battery_monitor_, BATTERY_PROP_CURRENT_AVG, INT32_MIN, _hidl_cb);
+ return Void();
+}
+
+Return<void> Health::getCapacity(getCapacity_cb _hidl_cb) {
+ getProperty(battery_monitor_, BATTERY_PROP_CAPACITY, INT32_MIN, _hidl_cb);
+ return Void();
+}
+
+Return<void> Health::getEnergyCounter(getEnergyCounter_cb _hidl_cb) {
+ getProperty(battery_monitor_, BATTERY_PROP_ENERGY_COUNTER, INT64_MIN, _hidl_cb);
+ return Void();
+}
+
+Return<void> Health::getChargeStatus(getChargeStatus_cb _hidl_cb) {
+ getProperty(battery_monitor_, BATTERY_PROP_BATTERY_STATUS, BatteryStatus::UNKNOWN, _hidl_cb);
+ return Void();
+}
+
+
+Return<Result> Health::update() {
+ if (!healthd_mode_ops || !healthd_mode_ops->battery_update) {
+ LOG(WARNING) << "health@2.0: update: not initialized. "
+ << "update() should not be called in charger / recovery.";
+ return Result::UNKNOWN;
+ }
+
+ // Retrieve all information and call healthd_mode_ops->battery_update, which calls
+ // notifyListeners.
+ bool chargerOnline = battery_monitor_->update();
+
+ // adjust uevent / wakealarm periods
+ healthd_battery_update_internal(chargerOnline);
+
+ return Result::SUCCESS;
+}
+
+void Health::notifyListeners(const HealthInfo& info) {
+ std::lock_guard<std::mutex> _lock(callbacks_lock_);
+ for (auto it = callbacks_.begin(); it != callbacks_.end();) {
+ auto ret = (*it)->healthInfoChanged(info);
+ if (!ret.isOk() && ret.isDeadObject()) {
+ it = callbacks_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+Return<void> Health::debug(const hidl_handle& handle, const hidl_vec<hidl_string>&) {
+ if (handle != nullptr && handle->numFds >= 1) {
+ int fd = handle->data[0];
+ battery_monitor_->dumpState(fd);
+ fsync(fd);
+ }
+ return Void();
+}
+
+void Health::serviceDied(uint64_t /* cookie */, const wp<IBase>& who) {
+ (void)unregisterCallbackInternal(who.promote());
+}
+
+// Methods from ::android::hidl::base::V1_0::IBase follow.
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace health
+} // namespace hardware
+} // namespace android
diff --git a/healthd/HealthService.cpp b/healthd/HealthService.cpp
new file mode 100644
index 0000000..e8a1a85
--- /dev/null
+++ b/healthd/HealthService.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2017 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.
+ */
+
+// This is a reference implementation for health@2.0 HAL. A vendor
+// can write its own HealthService.cpp with customized init and update functions.
+
+#define LOG_TAG "health@2.0/" HEALTH_INSTANCE_NAME
+#include <android-base/logging.h>
+
+#include <android/hardware/health/1.0/types.h>
+#include <hal_conversion.h>
+#include <health2/Health.h>
+#include <healthd/healthd.h>
+#include <hidl/HidlTransportSupport.h>
+
+using android::hardware::IPCThreadState;
+using android::hardware::configureRpcThreadpool;
+using android::hardware::health::V1_0::HealthInfo;
+using android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::implementation::Health;
+
+// see healthd_common.cpp
+android::sp<IHealth> gHealth;
+
+static int gBinderFd;
+
+extern int healthd_main(void);
+
+static void binder_event(uint32_t /*epevents*/) {
+ IPCThreadState::self()->handlePolledCommands();
+}
+
+// TODO(b/67463967): healthd_board_* functions should be removed in health@2.0
+int healthd_board_battery_update(struct android::BatteryProperties*)
+{
+ // return 0 to log periodic polled battery status to kernel log
+ return 0;
+}
+
+void healthd_mode_service_2_0_init(struct healthd_config* config) {
+ LOG(INFO) << LOG_TAG << " Hal is starting up...";
+
+ // Implementation-defined init logic goes here.
+ // 1. config->periodic_chores_interval_* variables
+ // 2. config->battery*Path variables
+ // 3. config->energyCounter. In this implementation, energyCounter is not defined.
+
+ configureRpcThreadpool(1, false /* callerWillJoin */);
+ IPCThreadState::self()->disableBackgroundScheduling(true);
+ IPCThreadState::self()->setupPolling(&gBinderFd);
+
+ if (gBinderFd >= 0) {
+ if (healthd_register_event(gBinderFd, binder_event))
+ LOG(ERROR) << LOG_TAG << ": Register for binder events failed";
+ }
+
+ gHealth = new ::android::hardware::health::V2_0::implementation::Health(config);
+ CHECK_EQ(gHealth->registerAsService(HEALTH_INSTANCE_NAME), android::OK)
+ << LOG_TAG << ": Failed to register HAL";
+
+ LOG(INFO) << LOG_TAG << ": Hal init done";
+}
+
+int healthd_mode_service_2_0_preparetowait(void) {
+ IPCThreadState::self()->flushCommands();
+ return -1;
+}
+
+void healthd_mode_service_2_0_heartbeat(void) {
+ // noop
+}
+
+void healthd_mode_service_2_0_battery_update(struct android::BatteryProperties* prop) {
+
+ // Implementation-defined update logic goes here. An implementation
+ // can make modifications to prop before broadcasting it to all callbacks.
+
+ HealthInfo info;
+ convertToHealthInfo(prop, info);
+ static_cast<Health*>(gHealth.get())->notifyListeners(info);
+}
+
+static struct healthd_mode_ops healthd_mode_service_2_0_ops = {
+ .init = healthd_mode_service_2_0_init,
+ .preparetowait = healthd_mode_service_2_0_preparetowait,
+ .heartbeat = healthd_mode_service_2_0_heartbeat,
+ .battery_update = healthd_mode_service_2_0_battery_update,
+};
+
+int main() {
+ healthd_mode_ops = &healthd_mode_service_2_0_ops;
+ LOG(INFO) << LOG_TAG << ": Hal starting main loop...";
+ return healthd_main();
+}
diff --git a/healthd/android.hardware.health@2.0-service.rc b/healthd/android.hardware.health@2.0-service.rc
new file mode 100644
index 0000000..8b86868
--- /dev/null
+++ b/healthd/android.hardware.health@2.0-service.rc
@@ -0,0 +1,4 @@
+service health-hal-2-0 /vendor/bin/hw/android.hardware.health@2.0-service
+ class hal
+ user system
+ group system
diff --git a/healthd/healthd_common.cpp b/healthd/healthd_common.cpp
index 6599919..19e600f 100644
--- a/healthd/healthd_common.cpp
+++ b/healthd/healthd_common.cpp
@@ -33,6 +33,10 @@
#include <sys/timerfd.h>
#include <utils/Errors.h>
+#ifdef HEALTHD_USE_HEALTH_2_0
+#include <health2/Health.h>
+#endif
+
using namespace android;
#ifndef BOARD_PERIODIC_CHORES_INTERVAL_FAST
@@ -84,9 +88,13 @@
static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST;
-static BatteryMonitor* gBatteryMonitor;
+#ifndef HEALTHD_USE_HEALTH_2_0
+static BatteryMonitor* gBatteryMonitor = nullptr;
+#else
+extern sp<::android::hardware::health::V2_0::IHealth> gHealth;
+#endif
-struct healthd_mode_ops *healthd_mode_ops;
+struct healthd_mode_ops *healthd_mode_ops = nullptr;
int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) {
struct epoll_event ev;
@@ -127,17 +135,87 @@
KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
}
+#ifdef HEALTHD_USE_HEALTH_2_0
+status_t convertStatus(android::hardware::health::V2_0::Result r) {
+ using android::hardware::health::V2_0::Result;
+ switch(r) {
+ case Result::SUCCESS: return OK;
+ case Result::NOT_SUPPORTED: return BAD_VALUE;
+ case Result::NOT_FOUND: return NAME_NOT_FOUND;
+ case Result::CALLBACK_DIED: return DEAD_OBJECT;
+ case Result::UNKNOWN: // fallthrough
+ default:
+ return UNKNOWN_ERROR;
+ }
+}
+#endif
+
status_t healthd_get_property(int id, struct BatteryProperty *val) {
+#ifndef HEALTHD_USE_HEALTH_2_0
return gBatteryMonitor->getProperty(id, val);
+#else
+ using android::hardware::health::V1_0::BatteryStatus;
+ using android::hardware::health::V2_0::Result;
+ val->valueInt64 = INT64_MIN;
+ status_t err = UNKNOWN_ERROR;
+ switch (id) {
+ case BATTERY_PROP_CHARGE_COUNTER: {
+ gHealth->getChargeCounter([&](Result r, int32_t v) {
+ err = convertStatus(r);
+ val->valueInt64 = v;
+ });
+ break;
+ }
+ case BATTERY_PROP_CURRENT_NOW: {
+ gHealth->getCurrentNow([&](Result r, int32_t v) {
+ err = convertStatus(r);
+ val->valueInt64 = v;
+ });
+ break;
+ }
+ case BATTERY_PROP_CURRENT_AVG: {
+ gHealth->getCurrentAverage([&](Result r, int32_t v) {
+ err = convertStatus(r);
+ val->valueInt64 = v;
+ });
+ break;
+ }
+ case BATTERY_PROP_CAPACITY: {
+ gHealth->getCapacity([&](Result r, int32_t v) {
+ err = convertStatus(r);
+ val->valueInt64 = v;
+ });
+ break;
+ }
+ case BATTERY_PROP_ENERGY_COUNTER: {
+ gHealth->getEnergyCounter([&](Result r, int64_t v) {
+ err = convertStatus(r);
+ val->valueInt64 = v;
+ });
+ break;
+ }
+ case BATTERY_PROP_BATTERY_STATUS: {
+ gHealth->getChargeStatus([&](Result r, BatteryStatus v) {
+ err = convertStatus(r);
+ val->valueInt64 = static_cast<int64_t>(v);
+ });
+ break;
+ }
+ default: {
+ err = BAD_VALUE;
+ break;
+ }
+ }
+ return err;
+#endif
}
-void healthd_battery_update(void) {
+void healthd_battery_update_internal(bool charger_online) {
// Fast wake interval when on charger (watch for overheat);
// slow wake interval when on battery (watch for drained battery).
- int new_wake_interval = gBatteryMonitor->update() ?
- healthd_config.periodic_chores_interval_fast :
- healthd_config.periodic_chores_interval_slow;
+ int new_wake_interval = charger_online ? healthd_config.periodic_chores_interval_fast
+ : healthd_config.periodic_chores_interval_slow;
if (new_wake_interval != wakealarm_wake_interval)
wakealarm_set_interval(new_wake_interval);
@@ -155,8 +233,25 @@
-1 : healthd_config.periodic_chores_interval_fast * 1000;
}
+void healthd_battery_update(void) {
+#ifndef HEALTHD_USE_HEALTH_2_0
+ healthd_battery_update_internal(gBatteryMonitor->update());
+#else
+ gHealth->update();
+#endif
+}
+
void healthd_dump_battery_state(int fd) {
+#ifndef HEALTHD_USE_HEALTH_2_0
gBatteryMonitor->dumpState(fd);
+#else
+ native_handle_t* nativeHandle = native_handle_create(1, 0);
+ nativeHandle->data[0] = fd;
+ ::android::hardware::hidl_handle handle;
+ handle.setTo(nativeHandle, true /* shouldOwn */);
+ gHealth->debug(handle, {} /* options */);
+#endif
+
fsync(fd);
}
@@ -273,12 +368,21 @@
return -1;
}
+#ifndef HEALTHD_USE_HEALTH_2_0
healthd_board_init(&healthd_config);
+#else
+ // healthd_board_* functions are removed in health@2.0
+#endif
+
healthd_mode_ops->init(&healthd_config);
wakealarm_init();
uevent_init();
+
+#ifndef HEALTHD_USE_HEALTH_2_0
gBatteryMonitor = new BatteryMonitor();
gBatteryMonitor->init(&healthd_config);
+#endif
+
return 0;
}
diff --git a/healthd/include/health2/Health.h b/healthd/include/health2/Health.h
new file mode 100644
index 0000000..4e78380
--- /dev/null
+++ b/healthd/include/health2/Health.h
@@ -0,0 +1,61 @@
+#ifndef ANDROID_HARDWARE_HEALTH_V2_0_HEALTH_H
+#define ANDROID_HARDWARE_HEALTH_V2_0_HEALTH_H
+
+#include <memory>
+#include <vector>
+
+#include <android/hardware/health/1.0/types.h>
+#include <android/hardware/health/2.0/IHealth.h>
+#include <healthd/BatteryMonitor.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace health {
+namespace V2_0 {
+namespace implementation {
+
+using V1_0::BatteryStatus;
+using V1_0::HealthInfo;
+
+using ::android::hidl::base::V1_0::IBase;
+
+struct Health : public IHealth, hidl_death_recipient {
+ public:
+ Health(struct healthd_config* c);
+
+ // TODO(b/62229583): clean up and hide these functions.
+ void notifyListeners(const HealthInfo& info);
+
+ // Methods from IHealth follow.
+ Return<Result> registerCallback(const sp<IHealthInfoCallback>& callback) override;
+ Return<Result> unregisterCallback(const sp<IHealthInfoCallback>& callback) override;
+ Return<Result> update() override;
+ Return<void> getChargeCounter(getChargeCounter_cb _hidl_cb) override;
+ Return<void> getCurrentNow(getCurrentNow_cb _hidl_cb) override;
+ Return<void> getCurrentAverage(getCurrentAverage_cb _hidl_cb) override;
+ Return<void> getCapacity(getCapacity_cb _hidl_cb) override;
+ Return<void> getEnergyCounter(getEnergyCounter_cb _hidl_cb) override;
+ Return<void> getChargeStatus(getChargeStatus_cb _hidl_cb) override;
+
+ // Methods from ::android::hidl::base::V1_0::IBase follow.
+ Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& args) override;
+
+ void serviceDied(uint64_t cookie, const wp<IBase>& /* who */) override;
+
+ private:
+ std::mutex callbacks_lock_;
+ std::vector<sp<IHealthInfoCallback>> callbacks_;
+ std::unique_ptr<BatteryMonitor> battery_monitor_;
+
+ bool unregisterCallbackInternal(const sp<IBase>& cb);
+
+};
+
+} // namespace implementation
+} // namespace V2_0
+} // namespace health
+} // namespace hardware
+} // namespace android
+
+#endif // ANDROID_HARDWARE_HEALTH_V2_0_HEALTH_H
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index 8865a7d..f9067cc 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -18,7 +18,6 @@
#define HEALTHD_BATTERYMONITOR_H
#include <batteryservice/BatteryService.h>
-#include <binder/IInterface.h>
#include <utils/String8.h>
#include <utils/Vector.h>
diff --git a/init/Android.bp b/init/Android.bp
index 0e580fc..e906771 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -78,6 +78,8 @@
"security.cpp",
"selinux.cpp",
"service.cpp",
+ "subcontext.cpp",
+ "subcontext.proto",
"rlimit_parser.cpp",
"tokenizer.cpp",
"uevent_listener.cpp",
@@ -173,6 +175,7 @@
"result_test.cpp",
"rlimit_parser_test.cpp",
"service_test.cpp",
+ "subcontext_test.cpp",
"ueventd_test.cpp",
"util_test.cpp",
],
@@ -188,4 +191,22 @@
],
}
+cc_benchmark {
+ name: "init_benchmarks",
+ defaults: ["init_defaults"],
+ srcs: [
+ "subcontext_benchmark.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ ],
+ static_libs: [
+ "libinit",
+ "libselinux",
+ "libcrypto",
+ "libprotobuf-cpp-lite",
+ ],
+}
+
subdirs = ["*"]
diff --git a/init/action.cpp b/init/action.cpp
index 60204a8..2617d00 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -24,34 +24,48 @@
#include "util.h"
using android::base::Join;
+using android::base::StartsWith;
namespace android {
namespace init {
-Command::Command(BuiltinFunction f, const std::vector<std::string>& args, int line)
- : func_(f), args_(args), line_(line) {}
+Result<Success> RunBuiltinFunction(const BuiltinFunction& function,
+ const std::vector<std::string>& args,
+ const std::string& context) {
+ auto builtin_arguments = BuiltinArguments(context);
-Result<Success> Command::InvokeFunc() const {
- std::vector<std::string> expanded_args;
- expanded_args.resize(args_.size());
- expanded_args[0] = args_[0];
- for (std::size_t i = 1; i < args_.size(); ++i) {
- if (!expand_props(args_[i], &expanded_args[i])) {
- return Error() << "cannot expand '" << args_[i] << "'";
+ builtin_arguments.args.resize(args.size());
+ builtin_arguments.args[0] = args[0];
+ for (std::size_t i = 1; i < args.size(); ++i) {
+ if (!expand_props(args[i], &builtin_arguments.args[i])) {
+ return Error() << "cannot expand '" << args[i] << "'";
}
}
- return func_(expanded_args);
+ return function(builtin_arguments);
+}
+
+Command::Command(BuiltinFunction f, bool execute_in_subcontext,
+ const std::vector<std::string>& args, int line)
+ : func_(std::move(f)), execute_in_subcontext_(execute_in_subcontext), args_(args), line_(line) {}
+
+Result<Success> Command::InvokeFunc(Subcontext* subcontext) const {
+ if (execute_in_subcontext_ && subcontext) {
+ return subcontext->Execute(args_);
+ } else {
+ const std::string& context = subcontext ? subcontext->context() : kInitContext;
+ return RunBuiltinFunction(func_, args_, context);
+ }
}
std::string Command::BuildCommandString() const {
return Join(args_, ' ');
}
-Action::Action(bool oneshot, const std::string& filename, int line)
- : oneshot_(oneshot), filename_(filename), line_(line) {}
+Action::Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line)
+ : oneshot_(oneshot), subcontext_(subcontext), filename_(filename), line_(line) {}
-const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
+const KeywordFunctionMap* Action::function_map_ = nullptr;
Result<Success> Action::AddCommand(const std::vector<std::string>& args, int line) {
if (!function_map_) {
@@ -61,12 +75,12 @@
auto function = function_map_->FindFunction(args);
if (!function) return Error() << function.error();
- AddCommand(*function, args, line);
+ commands_.emplace_back(function->second, function->first, args, line);
return Success();
}
void Action::AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line) {
- commands_.emplace_back(f, args, line);
+ commands_.emplace_back(f, false, args, line);
}
std::size_t Action::NumCommands() const {
@@ -88,7 +102,7 @@
void Action::ExecuteCommand(const Command& command) const {
android::base::Timer t;
- auto result = command.InvokeFunc();
+ auto result = command.InvokeFunc(subcontext_);
auto duration = t.duration();
// There are many legacy paths in rootdir/init.rc that will virtually never exist on a new
@@ -261,7 +275,7 @@
}
void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
- auto action = std::make_unique<Action>(true, "<Builtin Action>", 0);
+ auto action = std::make_unique<Action>(true, nullptr, "<Builtin Action>", 0);
std::vector<std::string> name_vector{name};
if (auto result = action->InitSingleTrigger(name); !result) {
@@ -341,7 +355,17 @@
return Error() << "Actions must have a trigger";
}
- auto action = std::make_unique<Action>(false, filename, line);
+ Subcontext* action_subcontext = nullptr;
+ if (subcontexts_) {
+ for (auto& subcontext : *subcontexts_) {
+ if (StartsWith(filename, subcontext.path_prefix().c_str())) {
+ action_subcontext = &subcontext;
+ break;
+ }
+ }
+ }
+
+ auto action = std::make_unique<Action>(false, action_subcontext, filename, line);
if (auto result = action->InitTriggers(triggers); !result) {
return Error() << "InitTriggers() failed: " << result.error();
diff --git a/init/action.h b/init/action.h
index d977f82..cdfc6a0 100644
--- a/init/action.h
+++ b/init/action.h
@@ -27,21 +27,27 @@
#include "keyword_map.h"
#include "parser.h"
#include "result.h"
+#include "subcontext.h"
namespace android {
namespace init {
+Result<Success> RunBuiltinFunction(const BuiltinFunction& function,
+ const std::vector<std::string>& args, const std::string& context);
+
class Command {
public:
- Command(BuiltinFunction f, const std::vector<std::string>& args, int line);
+ Command(BuiltinFunction f, bool execute_in_subcontext, const std::vector<std::string>& args,
+ int line);
- Result<Success> InvokeFunc() const;
+ Result<Success> InvokeFunc(Subcontext* subcontext) const;
std::string BuildCommandString() const;
int line() const { return line_; }
private:
BuiltinFunction func_;
+ bool execute_in_subcontext_;
std::vector<std::string> args_;
int line_;
};
@@ -52,7 +58,7 @@
class Action {
public:
- explicit Action(bool oneshot, const std::string& filename, int line);
+ Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line);
Result<Success> AddCommand(const std::vector<std::string>& args, int line);
void AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line);
@@ -70,12 +76,11 @@
bool oneshot() const { return oneshot_; }
const std::string& filename() const { return filename_; }
int line() const { return line_; }
- static void set_function_map(const KeywordMap<BuiltinFunction>* function_map) {
+ static void set_function_map(const KeywordFunctionMap* function_map) {
function_map_ = function_map;
}
-
-private:
+ private:
void ExecuteCommand(const Command& command) const;
bool CheckPropertyTriggers(const std::string& name = "",
const std::string& value = "") const;
@@ -85,9 +90,10 @@
std::string event_trigger_;
std::vector<Command> commands_;
bool oneshot_;
+ Subcontext* subcontext_;
std::string filename_;
int line_;
- static const KeywordMap<BuiltinFunction>* function_map_;
+ static const KeywordFunctionMap* function_map_;
};
class ActionManager {
@@ -119,8 +125,8 @@
class ActionParser : public SectionParser {
public:
- ActionParser(ActionManager* action_manager)
- : action_manager_(action_manager), action_(nullptr) {}
+ ActionParser(ActionManager* action_manager, std::vector<Subcontext>* subcontexts)
+ : action_manager_(action_manager), subcontexts_(subcontexts), action_(nullptr) {}
Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line) override;
Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
@@ -128,6 +134,7 @@
private:
ActionManager* action_manager_;
+ std::vector<Subcontext>* subcontexts_;
std::unique_ptr<Action> action_;
};
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index ec84317..379b4fa 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -191,7 +191,7 @@
return Success();
}
-Result<Success> do_bootchart(const std::vector<std::string>& args) {
+Result<Success> do_bootchart(const BuiltinArguments& args) {
if (args[1] == "start") return do_bootchart_start();
return do_bootchart_stop();
}
diff --git a/init/bootchart.h b/init/bootchart.h
index f614f71..05474ca 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -20,12 +20,13 @@
#include <string>
#include <vector>
+#include "builtin_arguments.h"
#include "result.h"
namespace android {
namespace init {
-Result<Success> do_bootchart(const std::vector<std::string>& args);
+Result<Success> do_bootchart(const BuiltinArguments& args);
} // namespace init
} // namespace android
diff --git a/init/builtin_arguments.h b/init/builtin_arguments.h
new file mode 100644
index 0000000..1742b78
--- /dev/null
+++ b/init/builtin_arguments.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 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_BUILTIN_ARGUMENTS_H
+#define _INIT_BUILTIN_ARGUMENTS_H
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace init {
+
+struct BuiltinArguments {
+ BuiltinArguments(const std::string& context) : context(context) {}
+ BuiltinArguments(std::vector<std::string> args, const std::string& context)
+ : args(std::move(args)), context(context) {}
+
+ const std::string& operator[](std::size_t i) const { return args[i]; }
+ auto begin() const { return args.begin(); }
+ auto end() const { return args.end(); }
+ auto size() const { return args.size(); }
+
+ std::vector<std::string> args;
+ const std::string& context;
+};
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 0fef883..9be274b 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -66,6 +66,7 @@
#include "reboot.h"
#include "rlimit_parser.h"
#include "service.h"
+#include "subcontext.h"
#include "util.h"
using namespace std::literals::string_literals;
@@ -95,36 +96,43 @@
}
}
-static Result<Success> do_class_start(const std::vector<std::string>& args) {
+static Result<Success> do_class_start(const BuiltinArguments& args) {
// Starting a class does not start services which are explicitly disabled.
// They must be started individually.
- ForEachServiceInClass(args[1], &Service::StartIfNotDisabled);
+ for (const auto& service : ServiceList::GetInstance()) {
+ if (service->classnames().count(args[1])) {
+ if (auto result = service->StartIfNotDisabled(); !result) {
+ LOG(ERROR) << "Could not start service '" << service->name()
+ << "' as part of class '" << args[1] << "': " << result.error();
+ }
+ }
+ }
return Success();
}
-static Result<Success> do_class_stop(const std::vector<std::string>& args) {
+static Result<Success> do_class_stop(const BuiltinArguments& args) {
ForEachServiceInClass(args[1], &Service::Stop);
return Success();
}
-static Result<Success> do_class_reset(const std::vector<std::string>& args) {
+static Result<Success> do_class_reset(const BuiltinArguments& args) {
ForEachServiceInClass(args[1], &Service::Reset);
return Success();
}
-static Result<Success> do_class_restart(const std::vector<std::string>& args) {
+static Result<Success> do_class_restart(const BuiltinArguments& args) {
ForEachServiceInClass(args[1], &Service::Restart);
return Success();
}
-static Result<Success> do_domainname(const std::vector<std::string>& args) {
+static Result<Success> do_domainname(const BuiltinArguments& args) {
if (auto result = WriteFile("/proc/sys/kernel/domainname", args[1]); !result) {
return Error() << "Unable to write to /proc/sys/kernel/domainname: " << result.error();
}
return Success();
}
-static Result<Success> do_enable(const std::vector<std::string>& args) {
+static Result<Success> do_enable(const BuiltinArguments& args) {
Service* svc = ServiceList::GetInstance().FindService(args[1]);
if (!svc) return Error() << "Could not find service";
@@ -135,8 +143,8 @@
return Success();
}
-static Result<Success> do_exec(const std::vector<std::string>& args) {
- auto service = Service::MakeTemporaryOneshotService(args);
+static Result<Success> do_exec(const BuiltinArguments& args) {
+ auto service = Service::MakeTemporaryOneshotService(args.args);
if (!service) {
return Error() << "Could not create exec service";
}
@@ -148,8 +156,8 @@
return Success();
}
-static Result<Success> do_exec_background(const std::vector<std::string>& args) {
- auto service = Service::MakeTemporaryOneshotService(args);
+static Result<Success> do_exec_background(const BuiltinArguments& args) {
+ auto service = Service::MakeTemporaryOneshotService(args.args);
if (!service) {
return Error() << "Could not create exec background service";
}
@@ -161,7 +169,7 @@
return Success();
}
-static Result<Success> do_exec_start(const std::vector<std::string>& args) {
+static Result<Success> do_exec_start(const BuiltinArguments& args) {
Service* service = ServiceList::GetInstance().FindService(args[1]);
if (!service) {
return Error() << "Service not found";
@@ -174,21 +182,21 @@
return Success();
}
-static Result<Success> do_export(const std::vector<std::string>& args) {
+static Result<Success> do_export(const BuiltinArguments& args) {
if (setenv(args[1].c_str(), args[2].c_str(), 1) == -1) {
return ErrnoError() << "setenv() failed";
}
return Success();
}
-static Result<Success> do_hostname(const std::vector<std::string>& args) {
+static Result<Success> do_hostname(const BuiltinArguments& args) {
if (auto result = WriteFile("/proc/sys/kernel/hostname", args[1]); !result) {
return Error() << "Unable to write to /proc/sys/kernel/hostname: " << result.error();
}
return Success();
}
-static Result<Success> do_ifup(const std::vector<std::string>& args) {
+static Result<Success> do_ifup(const BuiltinArguments& args) {
struct ifreq ifr;
strlcpy(ifr.ifr_name, args[1].c_str(), IFNAMSIZ);
@@ -209,7 +217,7 @@
return Success();
}
-static Result<Success> do_insmod(const std::vector<std::string>& args) {
+static Result<Success> do_insmod(const BuiltinArguments& args) {
int flags = 0;
auto it = args.begin() + 1;
@@ -231,7 +239,7 @@
}
// mkdir <path> [mode] [owner] [group]
-static Result<Success> do_mkdir(const std::vector<std::string>& args) {
+static Result<Success> do_mkdir(const BuiltinArguments& args) {
mode_t mode = 0755;
if (args.size() >= 3) {
mode = std::strtoul(args[2].c_str(), 0, 8);
@@ -287,7 +295,7 @@
}
/* umount <path> */
-static Result<Success> do_umount(const std::vector<std::string>& args) {
+static Result<Success> do_umount(const BuiltinArguments& args) {
if (umount(args[1].c_str()) < 0) {
return ErrnoError() << "umount() failed";
}
@@ -319,7 +327,7 @@
#define DATA_MNT_POINT "/data"
/* mount <type> <device> <path> <flags ...> <options> */
-static Result<Success> do_mount(const std::vector<std::string>& args) {
+static Result<Success> do_mount(const BuiltinArguments& args) {
const char* options = nullptr;
unsigned flags = 0;
bool wait = false;
@@ -530,7 +538,7 @@
* This function might request a reboot, in which case it will
* not return.
*/
-static Result<Success> do_mount_all(const std::vector<std::string>& args) {
+static Result<Success> do_mount_all(const BuiltinArguments& args) {
std::size_t na = 0;
bool import_rc = true;
bool queue_event = true;
@@ -563,7 +571,7 @@
if (import_rc) {
/* Paths of .rc files are specified at the 2nd argument and beyond */
- import_late(args, 2, path_arg_end);
+ import_late(args.args, 2, path_arg_end);
}
if (queue_event) {
@@ -578,7 +586,7 @@
return Success();
}
-static Result<Success> do_swapon_all(const std::vector<std::string>& args) {
+static Result<Success> do_swapon_all(const BuiltinArguments& args) {
struct fstab *fstab;
int ret;
@@ -590,13 +598,13 @@
return Success();
}
-static Result<Success> do_setprop(const std::vector<std::string>& args) {
+static Result<Success> do_setprop(const BuiltinArguments& args) {
property_set(args[1], args[2]);
return Success();
}
-static Result<Success> do_setrlimit(const std::vector<std::string>& args) {
- auto rlimit = ParseRlimit(args);
+static Result<Success> do_setrlimit(const BuiltinArguments& args) {
+ auto rlimit = ParseRlimit(args.args);
if (!rlimit) return rlimit.error();
if (setrlimit(rlimit->first, &rlimit->second) == -1) {
@@ -605,7 +613,7 @@
return Success();
}
-static Result<Success> do_start(const std::vector<std::string>& args) {
+static Result<Success> do_start(const BuiltinArguments& args) {
Service* svc = ServiceList::GetInstance().FindService(args[1]);
if (!svc) return Error() << "service " << args[1] << " not found";
if (auto result = svc->Start(); !result) {
@@ -614,26 +622,26 @@
return Success();
}
-static Result<Success> do_stop(const std::vector<std::string>& args) {
+static Result<Success> do_stop(const BuiltinArguments& args) {
Service* svc = ServiceList::GetInstance().FindService(args[1]);
if (!svc) return Error() << "service " << args[1] << " not found";
svc->Stop();
return Success();
}
-static Result<Success> do_restart(const std::vector<std::string>& args) {
+static Result<Success> do_restart(const BuiltinArguments& args) {
Service* svc = ServiceList::GetInstance().FindService(args[1]);
if (!svc) return Error() << "service " << args[1] << " not found";
svc->Restart();
return Success();
}
-static Result<Success> do_trigger(const std::vector<std::string>& args) {
+static Result<Success> do_trigger(const BuiltinArguments& args) {
ActionManager::GetInstance().QueueEventTrigger(args[1]);
return Success();
}
-static Result<Success> do_symlink(const std::vector<std::string>& args) {
+static Result<Success> do_symlink(const BuiltinArguments& args) {
if (symlink(args[1].c_str(), args[2].c_str()) < 0) {
// The symlink builtin is often used to create symlinks for older devices to be backwards
// compatible with new paths, therefore we skip reporting this error.
@@ -645,21 +653,21 @@
return Success();
}
-static Result<Success> do_rm(const std::vector<std::string>& args) {
+static Result<Success> do_rm(const BuiltinArguments& args) {
if (unlink(args[1].c_str()) < 0) {
return ErrnoError() << "unlink() failed";
}
return Success();
}
-static Result<Success> do_rmdir(const std::vector<std::string>& args) {
+static Result<Success> do_rmdir(const BuiltinArguments& args) {
if (rmdir(args[1].c_str()) < 0) {
return ErrnoError() << "rmdir() failed";
}
return Success();
}
-static Result<Success> do_sysclktz(const std::vector<std::string>& args) {
+static Result<Success> do_sysclktz(const BuiltinArguments& args) {
struct timezone tz = {};
if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) {
return Error() << "Unable to parse mins_west_of_gmt";
@@ -671,7 +679,7 @@
return Success();
}
-static Result<Success> do_verity_load_state(const std::vector<std::string>& args) {
+static Result<Success> do_verity_load_state(const BuiltinArguments& args) {
int mode = -1;
bool loaded = fs_mgr_load_verity_state(&mode);
if (loaded && mode != VERITY_MODE_DEFAULT) {
@@ -687,14 +695,14 @@
property_set("partition."s + mount_point + ".verified", std::to_string(mode));
}
-static Result<Success> do_verity_update_state(const std::vector<std::string>& args) {
+static Result<Success> do_verity_update_state(const BuiltinArguments& args) {
if (!fs_mgr_update_verity_state(verity_update_property)) {
return Error() << "fs_mgr_update_verity_state() failed";
}
return Success();
}
-static Result<Success> do_write(const std::vector<std::string>& args) {
+static Result<Success> do_write(const BuiltinArguments& args) {
if (auto result = WriteFile(args[1], args[2]); !result) {
return Error() << "Unable to write to file '" << args[1] << "': " << result.error();
}
@@ -725,7 +733,7 @@
return Success();
}
-static Result<Success> do_readahead(const std::vector<std::string>& args) {
+static Result<Success> do_readahead(const BuiltinArguments& args) {
struct stat sb;
if (stat(args[1].c_str(), &sb)) {
@@ -784,7 +792,7 @@
return Success();
}
-static Result<Success> do_copy(const std::vector<std::string>& args) {
+static Result<Success> do_copy(const BuiltinArguments& args) {
auto file_contents = ReadFile(args[1]);
if (!file_contents) {
return Error() << "Could not read input file '" << args[1] << "': " << file_contents.error();
@@ -796,7 +804,7 @@
return Success();
}
-static Result<Success> do_chown(const std::vector<std::string>& args) {
+static Result<Success> do_chown(const BuiltinArguments& args) {
auto uid = DecodeUid(args[1]);
if (!uid) {
return Error() << "Unable to decode UID for '" << args[1] << "': " << uid.error();
@@ -833,7 +841,7 @@
return mode;
}
-static Result<Success> do_chmod(const std::vector<std::string>& args) {
+static Result<Success> do_chmod(const BuiltinArguments& args) {
mode_t mode = get_mode(args[1].c_str());
if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
return ErrnoError() << "fchmodat() failed";
@@ -841,7 +849,7 @@
return Success();
}
-static Result<Success> do_restorecon(const std::vector<std::string>& args) {
+static Result<Success> do_restorecon(const BuiltinArguments& args) {
int ret = 0;
struct flag_type {const char* name; int value;};
@@ -883,13 +891,13 @@
return Success();
}
-static Result<Success> do_restorecon_recursive(const std::vector<std::string>& args) {
- std::vector<std::string> non_const_args(args);
+static Result<Success> do_restorecon_recursive(const BuiltinArguments& args) {
+ std::vector<std::string> non_const_args(args.args);
non_const_args.insert(std::next(non_const_args.begin()), "--recursive");
- return do_restorecon(non_const_args);
+ return do_restorecon({std::move(non_const_args), args.context});
}
-static Result<Success> do_loglevel(const std::vector<std::string>& args) {
+static Result<Success> do_loglevel(const BuiltinArguments& args) {
// TODO: support names instead/as well?
int log_level = -1;
android::base::ParseInt(args[1], &log_level);
@@ -910,17 +918,17 @@
return Success();
}
-static Result<Success> do_load_persist_props(const std::vector<std::string>& args) {
+static Result<Success> do_load_persist_props(const BuiltinArguments& args) {
load_persist_props();
return Success();
}
-static Result<Success> do_load_system_props(const std::vector<std::string>& args) {
+static Result<Success> do_load_system_props(const BuiltinArguments& args) {
load_system_props();
return Success();
}
-static Result<Success> do_wait(const std::vector<std::string>& args) {
+static Result<Success> do_wait(const BuiltinArguments& args) {
auto timeout = kCommandRetryTimeout;
if (args.size() == 3) {
int timeout_int;
@@ -937,7 +945,7 @@
return Success();
}
-static Result<Success> do_wait_for_prop(const std::vector<std::string>& args) {
+static Result<Success> do_wait_for_prop(const BuiltinArguments& args) {
const char* name = args[1].c_str();
const char* value = args[2].c_str();
size_t value_len = strlen(value);
@@ -958,7 +966,7 @@
return android::base::GetProperty("ro.crypto.type", "") == "file";
}
-static Result<Success> do_installkey(const std::vector<std::string>& args) {
+static Result<Success> do_installkey(const BuiltinArguments& args) {
if (!is_file_crypto()) return Success();
auto unencrypted_dir = args[1] + e4crypt_unencrypted_folder;
@@ -967,64 +975,66 @@
}
std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
"enablefilecrypto"};
- return do_exec(exec_args);
+ return do_exec({std::move(exec_args), args.context});
}
-static Result<Success> do_init_user0(const std::vector<std::string>& args) {
+static Result<Success> do_init_user0(const BuiltinArguments& args) {
std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
"init_user0"};
- return do_exec(exec_args);
+ return do_exec({std::move(exec_args), args.context});
}
const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
static const Map builtin_functions = {
- {"bootchart", {1, 1, do_bootchart}},
- {"chmod", {2, 2, do_chmod}},
- {"chown", {2, 3, do_chown}},
- {"class_reset", {1, 1, do_class_reset}},
- {"class_restart", {1, 1, do_class_restart}},
- {"class_start", {1, 1, do_class_start}},
- {"class_stop", {1, 1, do_class_stop}},
- {"copy", {2, 2, do_copy}},
- {"domainname", {1, 1, do_domainname}},
- {"enable", {1, 1, do_enable}},
- {"exec", {1, kMax, do_exec}},
- {"exec_background", {1, kMax, do_exec_background}},
- {"exec_start", {1, 1, do_exec_start}},
- {"export", {2, 2, do_export}},
- {"hostname", {1, 1, do_hostname}},
- {"ifup", {1, 1, do_ifup}},
- {"init_user0", {0, 0, do_init_user0}},
- {"insmod", {1, kMax, do_insmod}},
- {"installkey", {1, 1, do_installkey}},
- {"load_persist_props", {0, 0, do_load_persist_props}},
- {"load_system_props", {0, 0, do_load_system_props}},
- {"loglevel", {1, 1, do_loglevel}},
- {"mkdir", {1, 4, do_mkdir}},
- {"mount_all", {1, kMax, do_mount_all}},
- {"mount", {3, kMax, do_mount}},
- {"umount", {1, 1, do_umount}},
- {"readahead", {1, 2, do_readahead}},
- {"restart", {1, 1, do_restart}},
- {"restorecon", {1, kMax, do_restorecon}},
- {"restorecon_recursive", {1, kMax, do_restorecon_recursive}},
- {"rm", {1, 1, do_rm}},
- {"rmdir", {1, 1, do_rmdir}},
- {"setprop", {2, 2, do_setprop}},
- {"setrlimit", {3, 3, do_setrlimit}},
- {"start", {1, 1, do_start}},
- {"stop", {1, 1, do_stop}},
- {"swapon_all", {1, 1, do_swapon_all}},
- {"symlink", {2, 2, do_symlink}},
- {"sysclktz", {1, 1, do_sysclktz}},
- {"trigger", {1, 1, do_trigger}},
- {"verity_load_state", {0, 0, do_verity_load_state}},
- {"verity_update_state", {0, 0, do_verity_update_state}},
- {"wait", {1, 2, do_wait}},
- {"wait_for_prop", {2, 2, do_wait_for_prop}},
- {"write", {2, 2, do_write}},
+ {"bootchart", {1, 1, {false, do_bootchart}}},
+ {"chmod", {2, 2, {true, do_chmod}}},
+ {"chown", {2, 3, {true, do_chown}}},
+ {"class_reset", {1, 1, {false, do_class_reset}}},
+ {"class_restart", {1, 1, {false, do_class_restart}}},
+ {"class_start", {1, 1, {false, do_class_start}}},
+ {"class_stop", {1, 1, {false, do_class_stop}}},
+ {"copy", {2, 2, {true, do_copy}}},
+ {"domainname", {1, 1, {true, do_domainname}}},
+ {"enable", {1, 1, {false, do_enable}}},
+ {"exec", {1, kMax, {false, do_exec}}},
+ {"exec_background", {1, kMax, {false, do_exec_background}}},
+ {"exec_start", {1, 1, {false, do_exec_start}}},
+ {"export", {2, 2, {false, do_export}}},
+ {"hostname", {1, 1, {true, do_hostname}}},
+ {"ifup", {1, 1, {true, do_ifup}}},
+ {"init_user0", {0, 0, {false, do_init_user0}}},
+ {"insmod", {1, kMax, {true, do_insmod}}},
+ {"installkey", {1, 1, {false, do_installkey}}},
+ {"load_persist_props", {0, 0, {false, do_load_persist_props}}},
+ {"load_system_props", {0, 0, {false, do_load_system_props}}},
+ {"loglevel", {1, 1, {false, do_loglevel}}},
+ {"mkdir", {1, 4, {true, do_mkdir}}},
+ {"mount_all", {1, kMax, {false, do_mount_all}}},
+ {"mount", {3, kMax, {false, do_mount}}},
+ {"umount", {1, 1, {false, do_umount}}},
+ {"readahead", {1, 2, {true, do_readahead}}},
+ {"restart", {1, 1, {false, do_restart}}},
+ {"restorecon", {1, kMax, {true, do_restorecon}}},
+ {"restorecon_recursive", {1, kMax, {true, do_restorecon_recursive}}},
+ {"rm", {1, 1, {true, do_rm}}},
+ {"rmdir", {1, 1, {true, do_rmdir}}},
+ // TODO: setprop should be run in the subcontext, but property service needs to be split
+ // out from init before that is possible.
+ {"setprop", {2, 2, {false, do_setprop}}},
+ {"setrlimit", {3, 3, {false, do_setrlimit}}},
+ {"start", {1, 1, {false, do_start}}},
+ {"stop", {1, 1, {false, do_stop}}},
+ {"swapon_all", {1, 1, {false, do_swapon_all}}},
+ {"symlink", {2, 2, {true, do_symlink}}},
+ {"sysclktz", {1, 1, {false, do_sysclktz}}},
+ {"trigger", {1, 1, {false, do_trigger}}},
+ {"verity_load_state", {0, 0, {false, do_verity_load_state}}},
+ {"verity_update_state", {0, 0, {false, do_verity_update_state}}},
+ {"wait", {1, 2, {true, do_wait}}},
+ {"wait_for_prop", {2, 2, {true, do_wait_for_prop}}},
+ {"write", {2, 2, {true, do_write}}},
};
// clang-format on
return builtin_functions;
diff --git a/init/builtins.h b/init/builtins.h
index f66ae19..814b2d5 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -22,14 +22,17 @@
#include <string>
#include <vector>
+#include "builtin_arguments.h"
#include "keyword_map.h"
#include "result.h"
namespace android {
namespace init {
-using BuiltinFunction = std::function<Result<Success>(const std::vector<std::string>&)>;
-class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
+using BuiltinFunction = std::function<Result<Success>(const BuiltinArguments&)>;
+
+using KeywordFunctionMap = KeywordMap<std::pair<bool, BuiltinFunction>>;
+class BuiltinFunctionMap : public KeywordFunctionMap {
public:
BuiltinFunctionMap() {}
diff --git a/init/init.cpp b/init/init.cpp
index ad045b1..51a98a2 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -84,6 +84,8 @@
std::vector<std::string> late_import_paths;
+static std::vector<Subcontext>* subcontexts;
+
void DumpState() {
ServiceList::GetInstance().DumpState();
ActionManager::GetInstance().DumpState();
@@ -92,8 +94,8 @@
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
Parser parser;
- parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list));
- parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager));
+ parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
return parser;
@@ -220,7 +222,7 @@
}
}
-static Result<Success> wait_for_coldboot_done_action(const std::vector<std::string>& args) {
+static Result<Success> wait_for_coldboot_done_action(const BuiltinArguments& args) {
Timer t;
LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE "...";
@@ -241,12 +243,12 @@
return Success();
}
-static Result<Success> keychord_init_action(const std::vector<std::string>& args) {
+static Result<Success> keychord_init_action(const BuiltinArguments& args) {
keychord_init();
return Success();
}
-static Result<Success> console_init_action(const std::vector<std::string>& args) {
+static Result<Success> console_init_action(const BuiltinArguments& args) {
std::string console = GetProperty("ro.boot.console", "");
if (!console.empty()) {
default_console = "/dev/" + console;
@@ -333,13 +335,13 @@
if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
}
-static Result<Success> property_enable_triggers_action(const std::vector<std::string>& args) {
+static Result<Success> property_enable_triggers_action(const BuiltinArguments& args) {
/* Enable property triggers. */
property_triggers_enabled = 1;
return Success();
}
-static Result<Success> queue_property_triggers_action(const std::vector<std::string>& args) {
+static Result<Success> queue_property_triggers_action(const BuiltinArguments& args) {
ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, "enable_property_trigger");
ActionManager::GetInstance().QueueAllPropertyActions();
return Success();
@@ -446,6 +448,12 @@
return watchdogd_main(argc, argv);
}
+ if (argc > 1 && !strcmp(argv[1], "subcontext")) {
+ InitKernelLogging(argv);
+ const BuiltinFunctionMap function_map;
+ return SubcontextMain(argc, argv, &function_map);
+ }
+
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
@@ -587,6 +595,8 @@
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
+ subcontexts = InitializeSubcontexts();
+
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
diff --git a/init/init_test.cpp b/init/init_test.cpp
index 27659f9..29a65ab 100644
--- a/init/init_test.cpp
+++ b/init/init_test.cpp
@@ -25,33 +25,12 @@
#include "import_parser.h"
#include "keyword_map.h"
#include "parser.h"
+#include "test_function_map.h"
#include "util.h"
namespace android {
namespace init {
-class TestFunctionMap : public KeywordMap<BuiltinFunction> {
- public:
- // Helper for argument-less functions
- using BuiltinFunctionNoArgs = std::function<void(void)>;
- void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
- Add(name, 0, 0, [function](const std::vector<std::string>&) {
- function();
- return Success();
- });
- }
-
- void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
- const BuiltinFunction function) {
- builtin_functions_[name] = make_tuple(min_parameters, max_parameters, function);
- }
-
- private:
- Map builtin_functions_ = {};
-
- const Map& map() const override { return builtin_functions_; }
-};
-
using ActionManagerCommand = std::function<void(ActionManager&)>;
void TestInit(const std::string& init_script_file, const TestFunctionMap& test_function_map,
@@ -61,7 +40,7 @@
Action::set_function_map(&test_function_map);
Parser parser;
- parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&am, nullptr));
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
ASSERT_TRUE(parser.ParseConfig(init_script_file));
@@ -171,14 +150,14 @@
ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));
int num_executed = 0;
- auto execute_command = [&num_executed](const std::vector<std::string>& args) {
+ auto execute_command = [&num_executed](const BuiltinArguments& args) {
EXPECT_EQ(2U, args.size());
EXPECT_EQ(++num_executed, std::stoi(args[1]));
return Success();
};
TestFunctionMap test_function_map;
- test_function_map.Add("execute", 1, 1, execute_command);
+ test_function_map.Add("execute", 1, 1, false, execute_command);
ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
std::vector<ActionManagerCommand> commands{trigger_boot};
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 18f493a..a4a20f3 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -86,8 +86,8 @@
mnt_type_(entry.mnt_type),
mnt_opts_(entry.mnt_opts) {}
- bool Umount() {
- int r = umount2(mnt_dir_.c_str(), 0);
+ bool Umount(bool force) {
+ int r = umount2(mnt_dir_.c_str(), force ? MNT_FORCE : 0);
if (r == 0) {
LOG(INFO) << "umounted " << mnt_fsname_ << ":" << mnt_dir_ << " opts " << mnt_opts_;
return true;
@@ -280,14 +280,15 @@
bool unmount_done = true;
if (emulated_devices.size() > 0) {
unmount_done = std::all_of(emulated_devices.begin(), emulated_devices.end(),
- [](auto& entry) { return entry.Umount(); });
+ [](auto& entry) { return entry.Umount(false); });
if (unmount_done) {
sync();
}
}
- unmount_done = std::all_of(block_devices.begin(), block_devices.end(),
- [](auto& entry) { return entry.Umount(); }) &&
- unmount_done;
+ unmount_done =
+ std::all_of(block_devices.begin(), block_devices.end(),
+ [&timeout](auto& entry) { return entry.Umount(timeout == 0ms); }) &&
+ unmount_done;
if (unmount_done) {
return UMOUNT_STAT_SUCCESS;
}
@@ -521,8 +522,7 @@
// Queue shutdown trigger first
ActionManager::GetInstance().QueueEventTrigger("shutdown");
// Queue built-in shutdown_done
- auto shutdown_handler = [cmd, command, reboot_target,
- run_fsck](const std::vector<std::string>&) {
+ auto shutdown_handler = [cmd, command, reboot_target, run_fsck](const BuiltinArguments&) {
DoReboot(cmd, command, reboot_target, run_fsck);
return Success();
};
diff --git a/init/security.cpp b/init/security.cpp
index aac8f2e..a3494a2 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -43,7 +43,7 @@
// devices/configurations where these I/O operations are blocking for a long
// time. We do not reboot or halt on failures, as this is a best-effort
// attempt.
-Result<Success> MixHwrngIntoLinuxRngAction(const std::vector<std::string>& args) {
+Result<Success> MixHwrngIntoLinuxRngAction(const BuiltinArguments&) {
unique_fd hwrandom_fd(
TEMP_FAILURE_RETRY(open("/dev/hw_random", O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
if (hwrandom_fd == -1) {
@@ -147,7 +147,7 @@
// 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS
// ec9ee4acd97c drivers: char: random: add get_random_long()
// 5ef11c35ce86 mm: ASLR: use get_random_long()
-Result<Success> SetMmapRndBitsAction(const std::vector<std::string>& args) {
+Result<Success> SetMmapRndBitsAction(const BuiltinArguments&) {
// values are arch-dependent
#if defined(USER_MODE_LINUX)
// uml does not support mmap_rnd_bits
@@ -187,7 +187,7 @@
// Set kptr_restrict to the highest available level.
//
// Aborts if unable to set this to an acceptable value.
-Result<Success> SetKptrRestrictAction(const std::vector<std::string>& args) {
+Result<Success> SetKptrRestrictAction(const BuiltinArguments&) {
std::string path = KPTR_RESTRICT_PATH;
if (!SetHighestAvailableOptionValue(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {
diff --git a/init/security.h b/init/security.h
index 31e5790..6f6b944 100644
--- a/init/security.h
+++ b/init/security.h
@@ -20,14 +20,15 @@
#include <string>
#include <vector>
+#include "builtin_arguments.h"
#include "result.h"
namespace android {
namespace init {
-Result<Success> MixHwrngIntoLinuxRngAction(const std::vector<std::string>& args);
-Result<Success> SetMmapRndBitsAction(const std::vector<std::string>& args);
-Result<Success> SetKptrRestrictAction(const std::vector<std::string>& args);
+Result<Success> MixHwrngIntoLinuxRngAction(const BuiltinArguments&);
+Result<Success> SetMmapRndBitsAction(const BuiltinArguments&);
+Result<Success> SetKptrRestrictAction(const BuiltinArguments&);
} // namespace init
} // namespace android
diff --git a/init/selinux.cpp b/init/selinux.cpp
index aefec14..4a3a271 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -328,7 +328,7 @@
std::vector<const char*> compile_args {
"/system/bin/secilc",
plat_policy_cil_file,
- "-M", "true", "-G", "-N",
+ "-m", "-M", "true", "-G", "-N",
// Target the highest policy language version supported by the kernel
"-c", version_as_string.c_str(),
mapping_file.c_str(),
diff --git a/init/service.cpp b/init/service.cpp
index 86b910a..b339bc0 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -155,13 +155,14 @@
unsigned long Service::next_start_order_ = 1;
bool Service::is_exec_service_running_ = false;
-Service::Service(const std::string& name, const std::vector<std::string>& args)
- : Service(name, 0, 0, 0, {}, 0, 0, "", args) {}
+Service::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
+ const std::vector<std::string>& args)
+ : Service(name, 0, 0, 0, {}, 0, 0, "", subcontext_for_restart_commands, args) {}
Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
const std::vector<gid_t>& supp_gids, const CapSet& capabilities,
unsigned namespace_flags, const std::string& seclabel,
- const std::vector<std::string>& args)
+ Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& args)
: name_(name),
classnames_({"default"}),
flags_(flags),
@@ -173,7 +174,7 @@
capabilities_(capabilities),
namespace_flags_(namespace_flags),
seclabel_(seclabel),
- onrestart_(false, "<Service '" + name + "' onrestart>", 0),
+ onrestart_(false, subcontext_for_restart_commands, "<Service '" + name + "' onrestart>", 0),
keychord_id_(0),
ioprio_class_(IoSchedClass_NONE),
ioprio_pri_(0),
@@ -1007,7 +1008,7 @@
}
return std::make_unique<Service>(name, flags, *uid, *gid, supp_gids, no_capabilities,
- namespace_flags, seclabel, str_args);
+ namespace_flags, seclabel, nullptr, str_args);
}
// Shutdown services in the opposite order that they were started.
@@ -1055,8 +1056,18 @@
return Error() << "ignored duplicate definition of service '" << name << "'";
}
+ Subcontext* restart_action_subcontext = nullptr;
+ if (subcontexts_) {
+ for (auto& subcontext : *subcontexts_) {
+ if (StartsWith(filename, subcontext.path_prefix().c_str())) {
+ restart_action_subcontext = &subcontext;
+ break;
+ }
+ }
+ }
+
std::vector<std::string> str_args(args.begin() + 2, args.end());
- service_ = std::make_unique<Service>(name, str_args);
+ service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args);
return Success();
}
diff --git a/init/service.h b/init/service.h
index 67542ca..89dd780 100644
--- a/init/service.h
+++ b/init/service.h
@@ -33,6 +33,7 @@
#include "descriptors.h"
#include "keyword_map.h"
#include "parser.h"
+#include "subcontext.h"
#define SVC_DISABLED 0x001 // do not autostart with class
#define SVC_ONESHOT 0x002 // do not restart on exit
@@ -60,12 +61,13 @@
class Service {
public:
- Service(const std::string& name, const std::vector<std::string>& args);
+ Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
+ const std::vector<std::string>& args);
Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
const std::vector<gid_t>& supp_gids, const CapSet& capabilities,
unsigned namespace_flags, const std::string& seclabel,
- const std::vector<std::string>& args);
+ Subcontext* subcontext_for_restart_commands, const std::vector<std::string>& args);
static std::unique_ptr<Service> MakeTemporaryOneshotService(const std::vector<std::string>& args);
@@ -237,7 +239,8 @@
class ServiceParser : public SectionParser {
public:
- ServiceParser(ServiceList* service_list) : service_list_(service_list), service_(nullptr) {}
+ ServiceParser(ServiceList* service_list, std::vector<Subcontext>* subcontexts)
+ : service_list_(service_list), subcontexts_(subcontexts), service_(nullptr) {}
Result<Success> ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line) override;
Result<Success> ParseLineSection(std::vector<std::string>&& args, int line) override;
@@ -247,6 +250,7 @@
bool IsValidName(const std::string& name) const;
ServiceList* service_list_;
+ std::vector<Subcontext>* subcontexts_;
std::unique_ptr<Service> service_;
};
diff --git a/init/service_test.cpp b/init/service_test.cpp
index 98d876f..b43c2e9 100644
--- a/init/service_test.cpp
+++ b/init/service_test.cpp
@@ -37,7 +37,8 @@
}
std::vector<std::string> dummy_args{"/bin/test"};
- Service* service_in_old_memory = new (old_memory) Service("test_old_memory", dummy_args);
+ Service* service_in_old_memory =
+ new (old_memory) Service("test_old_memory", nullptr, dummy_args);
EXPECT_EQ(0U, service_in_old_memory->flags());
EXPECT_EQ(0, service_in_old_memory->pid());
@@ -56,8 +57,8 @@
old_memory[i] = 0xFF;
}
- Service* service_in_old_memory2 = new (old_memory)
- Service("test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), CapSet(), 0U, "", dummy_args);
+ Service* service_in_old_memory2 = new (old_memory) Service(
+ "test_old_memory", 0U, 0U, 0U, std::vector<gid_t>(), CapSet(), 0U, "", nullptr, dummy_args);
EXPECT_EQ(0U, service_in_old_memory2->flags());
EXPECT_EQ(0, service_in_old_memory2->pid());
diff --git a/init/sigchld_handler.cpp b/init/sigchld_handler.cpp
index fa67199..072a0fb 100644
--- a/init/sigchld_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -60,22 +60,28 @@
// want the pid to remain valid throughout that (and potentially future) usages.
auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });
- if (PropertyChildReap(pid)) return true;
-
- Service* service = ServiceList::GetInstance().FindService(pid, &Service::pid);
-
std::string name;
std::string wait_string;
- if (service) {
- name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid);
- if (service->flags() & SVC_EXEC) {
- auto exec_duration = boot_clock::now() - service->time_started();
- auto exec_duration_ms =
- std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
- wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);
- }
+ Service* service = nullptr;
+
+ if (PropertyChildReap(pid)) {
+ name = "Async property child";
+ } else if (SubcontextChildReap(pid)) {
+ name = "Subcontext";
} else {
- name = StringPrintf("Untracked pid %d", pid);
+ service = ServiceList::GetInstance().FindService(pid, &Service::pid);
+
+ if (service) {
+ name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid);
+ if (service->flags() & SVC_EXEC) {
+ auto exec_duration = boot_clock::now() - service->time_started();
+ auto exec_duration_ms =
+ std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
+ wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);
+ }
+ } else {
+ name = StringPrintf("Untracked pid %d", pid);
+ }
}
auto status = siginfo.si_status;
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
new file mode 100644
index 0000000..927953d
--- /dev/null
+++ b/init/subcontext.cpp
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2017 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 "subcontext.h"
+
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <selinux/android.h>
+
+#include "action.h"
+#include "system/core/init/subcontext.pb.h"
+#include "util.h"
+
+using android::base::GetBoolProperty;
+using android::base::GetExecutablePath;
+using android::base::Join;
+using android::base::Socketpair;
+using android::base::Split;
+using android::base::StartsWith;
+using android::base::unique_fd;
+
+namespace android {
+namespace init {
+
+const std::string kInitContext = "u:r:init:s0";
+const std::string kVendorContext = "u:r:vendor_init:s0";
+
+namespace {
+
+constexpr size_t kBufferSize = 4096;
+
+Result<std::string> ReadMessage(int socket) {
+ char buffer[kBufferSize] = {};
+ auto result = TEMP_FAILURE_RETRY(recv(socket, buffer, sizeof(buffer), 0));
+ if (result <= 0) {
+ return ErrnoError();
+ }
+ return std::string(buffer, result);
+}
+
+template <typename T>
+Result<Success> SendMessage(int socket, const T& message) {
+ std::string message_string;
+ if (!message.SerializeToString(&message_string)) {
+ return Error() << "Unable to serialize message";
+ }
+
+ if (message_string.size() > kBufferSize) {
+ return Error() << "Serialized message too long to send";
+ }
+
+ if (auto result =
+ TEMP_FAILURE_RETRY(send(socket, message_string.c_str(), message_string.size(), 0));
+ result != static_cast<long>(message_string.size())) {
+ return ErrnoError() << "send() failed to send message contents";
+ }
+ return Success();
+}
+
+class SubcontextProcess {
+ public:
+ SubcontextProcess(const KeywordFunctionMap* function_map, std::string context, int init_fd)
+ : function_map_(function_map), context_(std::move(context)), init_fd_(init_fd){};
+ void MainLoop();
+
+ private:
+ void RunCommand(const SubcontextCommand::ExecuteCommand& execute_command,
+ SubcontextReply::ResultMessage* result_message) const;
+
+ const KeywordFunctionMap* function_map_;
+ const std::string context_;
+ const int init_fd_;
+};
+
+void SubcontextProcess::RunCommand(const SubcontextCommand::ExecuteCommand& execute_command,
+ SubcontextReply::ResultMessage* result_message) const {
+ // Need to use ArraySplice instead of this code.
+ auto args = std::vector<std::string>();
+ for (const auto& string : execute_command.args()) {
+ args.emplace_back(string);
+ }
+
+ auto map_result = function_map_->FindFunction(args);
+ Result<Success> result;
+ if (!map_result) {
+ result = Error() << "Cannot find command: " << map_result.error();
+ } else {
+ result = RunBuiltinFunction(map_result->second, args, context_);
+ }
+
+ if (result) {
+ result_message->set_success(true);
+ } else {
+ result_message->set_success(false);
+ result_message->set_error_string(result.error_string());
+ result_message->set_error_errno(result.error_errno());
+ }
+}
+
+void SubcontextProcess::MainLoop() {
+ pollfd ufd[1];
+ ufd[0].events = POLLIN;
+ ufd[0].fd = init_fd_;
+
+ while (true) {
+ ufd[0].revents = 0;
+ int nr = TEMP_FAILURE_RETRY(poll(ufd, arraysize(ufd), -1));
+ if (nr == 0) continue;
+ if (nr < 0) {
+ PLOG(FATAL) << "poll() of subcontext socket failed, continuing";
+ }
+
+ auto init_message = ReadMessage(init_fd_);
+ if (!init_message) {
+ LOG(FATAL) << "Could not read message from init: " << init_message.error();
+ }
+
+ auto subcontext_command = SubcontextCommand();
+ if (!subcontext_command.ParseFromString(*init_message)) {
+ LOG(FATAL) << "Unable to parse message from init";
+ }
+
+ auto reply = SubcontextReply();
+ switch (subcontext_command.command_case()) {
+ case SubcontextCommand::kExecuteCommand: {
+ RunCommand(subcontext_command.execute_command(), reply.mutable_result());
+ break;
+ }
+ default:
+ LOG(FATAL) << "Unknown message type from init: "
+ << subcontext_command.command_case();
+ }
+
+ if (auto result = SendMessage(init_fd_, reply); !result) {
+ LOG(FATAL) << "Failed to send message to init: " << result.error();
+ }
+ }
+}
+
+} // namespace
+
+int SubcontextMain(int argc, char** argv, const KeywordFunctionMap* function_map) {
+ if (argc < 4) LOG(FATAL) << "Fewer than 4 args specified to subcontext (" << argc << ")";
+
+ auto context = std::string(argv[2]);
+ auto init_fd = std::atoi(argv[3]);
+
+ auto subcontext_process = SubcontextProcess(function_map, context, init_fd);
+ subcontext_process.MainLoop();
+ return 0;
+}
+
+void Subcontext::Fork() {
+ unique_fd subcontext_socket;
+ if (!Socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, &socket_, &subcontext_socket)) {
+ LOG(FATAL) << "Could not create socket pair to communicate to subcontext";
+ return;
+ }
+
+ auto result = fork();
+
+ if (result == -1) {
+ LOG(FATAL) << "Could not fork subcontext";
+ } else if (result == 0) {
+ socket_.reset();
+
+ // We explicitly do not use O_CLOEXEC here, such that we can reference this FD by number
+ // in the subcontext process after we exec.
+ int child_fd = dup(subcontext_socket);
+ if (child_fd < 0) {
+ PLOG(FATAL) << "Could not dup child_fd";
+ }
+
+ if (setexeccon(context_.c_str()) < 0) {
+ PLOG(FATAL) << "Could not set execcon for '" << context_ << "'";
+ }
+
+ auto init_path = GetExecutablePath();
+ auto child_fd_string = std::to_string(child_fd);
+ const char* args[] = {init_path.c_str(), "subcontext", context_.c_str(),
+ child_fd_string.c_str(), nullptr};
+ execv(init_path.data(), const_cast<char**>(args));
+
+ PLOG(FATAL) << "Could not execv subcontext init";
+ } else {
+ subcontext_socket.reset();
+ pid_ = result;
+ LOG(INFO) << "Forked subcontext for '" << context_ << "' with pid " << pid_;
+ }
+}
+
+void Subcontext::Restart() {
+ LOG(ERROR) << "Restarting subcontext '" << context_ << "'";
+ if (pid_) {
+ kill(pid_, SIGKILL);
+ }
+ pid_ = 0;
+ socket_.reset();
+ Fork();
+}
+
+Result<Success> Subcontext::Execute(const std::vector<std::string>& args) {
+ auto subcontext_command = SubcontextCommand();
+ std::copy(
+ args.begin(), args.end(),
+ RepeatedPtrFieldBackInserter(subcontext_command.mutable_execute_command()->mutable_args()));
+
+ if (auto result = SendMessage(socket_, subcontext_command); !result) {
+ Restart();
+ return ErrnoError() << "Failed to send message to subcontext";
+ }
+
+ auto subcontext_message = ReadMessage(socket_);
+ if (!subcontext_message) {
+ Restart();
+ return Error() << "Failed to receive result from subcontext: " << subcontext_message.error();
+ }
+
+ auto subcontext_reply = SubcontextReply();
+ if (!subcontext_reply.ParseFromString(*subcontext_message)) {
+ Restart();
+ return Error() << "Unable to parse message from subcontext";
+ }
+
+ switch (subcontext_reply.reply_case()) {
+ case SubcontextReply::kResult: {
+ auto result = subcontext_reply.result();
+ if (result.success()) {
+ return Success();
+ } else {
+ return ResultError(result.error_string(), result.error_errno());
+ }
+ }
+ default:
+ return Error() << "Unknown message type from subcontext: "
+ << subcontext_reply.reply_case();
+ }
+}
+
+static std::vector<Subcontext> subcontexts;
+
+std::vector<Subcontext>* InitializeSubcontexts() {
+ if (GetBoolProperty("ro.init.subcontexts_enabled", false)) {
+ 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);
+ }
+ }
+ return &subcontexts;
+}
+
+bool SubcontextChildReap(pid_t pid) {
+ for (auto& subcontext : subcontexts) {
+ if (subcontext.pid() == pid) {
+ subcontext.Restart();
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/subcontext.h b/init/subcontext.h
new file mode 100644
index 0000000..ac77e08
--- /dev/null
+++ b/init/subcontext.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 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_SUBCONTEXT_H
+#define _INIT_SUBCONTEXT_H
+
+#include <signal.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+#include "builtins.h"
+
+namespace android {
+namespace init {
+
+extern const std::string kInitContext;
+extern const std::string kVendorContext;
+
+class Subcontext {
+ public:
+ Subcontext(std::string path_prefix, std::string context)
+ : path_prefix_(path_prefix), context_(std::move(context)) {
+ Fork();
+ }
+
+ Result<Success> Execute(const std::vector<std::string>& command);
+ void Restart();
+
+ const std::string& path_prefix() const { return path_prefix_; }
+ const std::string& context() const { return context_; }
+ pid_t pid() const { return pid_; }
+
+ private:
+ void Fork();
+
+ std::string path_prefix_;
+ std::string context_;
+ pid_t pid_;
+ android::base::unique_fd socket_;
+};
+
+// For testing, to kill the subcontext after the test has completed.
+class SubcontextKiller {
+ public:
+ SubcontextKiller(const Subcontext& subcontext) : subcontext_(subcontext) {}
+ ~SubcontextKiller() {
+ if (subcontext_.pid() > 0) {
+ kill(subcontext_.pid(), SIGTERM);
+ kill(subcontext_.pid(), SIGKILL);
+ }
+ }
+
+ private:
+ const Subcontext& subcontext_;
+};
+
+int SubcontextMain(int argc, char** argv, const KeywordFunctionMap* function_map);
+std::vector<Subcontext>* InitializeSubcontexts();
+bool SubcontextChildReap(pid_t pid);
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/subcontext.proto b/init/subcontext.proto
new file mode 100644
index 0000000..0d89734
--- /dev/null
+++ b/init/subcontext.proto
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+message SubcontextCommand {
+ message ExecuteCommand { repeated string args = 1; }
+ oneof command { ExecuteCommand execute_command = 1; }
+}
+
+message SubcontextReply {
+ message ResultMessage {
+ optional bool success = 1;
+ optional string error_string = 2;
+ optional int32 error_errno = 3;
+ }
+
+ oneof reply { ResultMessage result = 1; }
+}
\ No newline at end of file
diff --git a/init/subcontext_benchmark.cpp b/init/subcontext_benchmark.cpp
new file mode 100644
index 0000000..a62b959
--- /dev/null
+++ b/init/subcontext_benchmark.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 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 "subcontext.h"
+
+#include <benchmark/benchmark.h>
+
+#include "test_function_map.h"
+
+namespace android {
+namespace init {
+
+static void BenchmarkSuccess(benchmark::State& state) {
+ auto subcontext = Subcontext("path", kVendorContext);
+ auto subcontext_killer = SubcontextKiller(subcontext);
+
+ while (state.KeepRunning()) {
+ subcontext.Execute(std::vector<std::string>{"return_success"});
+ }
+}
+
+BENCHMARK(BenchmarkSuccess);
+
+TestFunctionMap BuildTestFunctionMap() {
+ TestFunctionMap test_function_map;
+ test_function_map.Add("return_success", 0, 0, true,
+ [](const BuiltinArguments& args) { return Success(); });
+
+ return test_function_map;
+}
+
+} // namespace init
+} // namespace android
+
+int main(int argc, char** argv) {
+ if (argc > 1 && !strcmp(basename(argv[1]), "subcontext")) {
+ auto test_function_map = android::init::BuildTestFunctionMap();
+ return android::init::SubcontextMain(argc, argv, &test_function_map);
+ }
+
+ ::benchmark::Initialize(&argc, argv);
+ if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1;
+ ::benchmark::RunSpecifiedBenchmarks();
+}
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
new file mode 100644
index 0000000..60b45b9
--- /dev/null
+++ b/init/subcontext_test.cpp
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2017 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 "subcontext.h"
+
+#include <unistd.h>
+
+#include <chrono>
+
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+
+#include "builtin_arguments.h"
+#include "test_function_map.h"
+
+using namespace std::literals;
+
+using android::base::GetProperty;
+using android::base::Join;
+using android::base::SetProperty;
+using android::base::Split;
+using android::base::WaitForProperty;
+
+namespace android {
+namespace init {
+
+TEST(subcontext, CheckDifferentPid) {
+ auto subcontext = Subcontext("path", kVendorContext);
+ auto subcontext_killer = SubcontextKiller(subcontext);
+
+ auto result = subcontext.Execute(std::vector<std::string>{"return_pids_as_error"});
+ ASSERT_FALSE(result);
+
+ auto pids = Split(result.error_string(), " ");
+ ASSERT_EQ(2U, pids.size());
+ auto our_pid = std::to_string(getpid());
+ EXPECT_NE(our_pid, pids[0]);
+ EXPECT_EQ(our_pid, pids[1]);
+}
+
+TEST(subcontext, SetProp) {
+ auto subcontext = Subcontext("path", kVendorContext);
+ auto subcontext_killer = SubcontextKiller(subcontext);
+
+ SetProperty("init.test.subcontext", "fail");
+ WaitForProperty("init.test.subcontext", "fail");
+
+ auto args = std::vector<std::string>{
+ "setprop",
+ "init.test.subcontext",
+ "success",
+ };
+ auto result = subcontext.Execute(args);
+ ASSERT_TRUE(result) << result.error();
+
+ EXPECT_TRUE(WaitForProperty("init.test.subcontext", "success", 10s));
+}
+
+TEST(subcontext, MultipleCommands) {
+ auto subcontext = Subcontext("path", kVendorContext);
+ auto subcontext_killer = SubcontextKiller(subcontext);
+
+ auto first_pid = subcontext.pid();
+
+ auto expected_words = std::vector<std::string>{
+ "this",
+ "is",
+ "a",
+ "test",
+ };
+
+ for (const auto& word : expected_words) {
+ auto args = std::vector<std::string>{
+ "add_word",
+ word,
+ };
+ auto result = subcontext.Execute(args);
+ ASSERT_TRUE(result) << result.error();
+ }
+
+ auto result = subcontext.Execute(std::vector<std::string>{"return_words_as_error"});
+ ASSERT_FALSE(result);
+ EXPECT_EQ(Join(expected_words, " "), result.error_string());
+ EXPECT_EQ(first_pid, subcontext.pid());
+}
+
+TEST(subcontext, RecoverAfterAbort) {
+ auto subcontext = Subcontext("path", kVendorContext);
+ auto subcontext_killer = SubcontextKiller(subcontext);
+
+ auto first_pid = subcontext.pid();
+
+ auto result = subcontext.Execute(std::vector<std::string>{"cause_log_fatal"});
+ ASSERT_FALSE(result);
+
+ auto result2 = subcontext.Execute(std::vector<std::string>{"generate_sane_error"});
+ ASSERT_FALSE(result2);
+ EXPECT_EQ("Sane error!", result2.error_string());
+ EXPECT_NE(subcontext.pid(), first_pid);
+}
+
+TEST(subcontext, ContextString) {
+ auto subcontext = Subcontext("path", kVendorContext);
+ auto subcontext_killer = SubcontextKiller(subcontext);
+
+ auto result = subcontext.Execute(std::vector<std::string>{"return_context_as_error"});
+ ASSERT_FALSE(result);
+ ASSERT_EQ(kVendorContext, result.error_string());
+}
+
+TestFunctionMap BuildTestFunctionMap() {
+ TestFunctionMap test_function_map;
+ // For CheckDifferentPid
+ test_function_map.Add("return_pids_as_error", 0, 0, true,
+ [](const BuiltinArguments& args) -> Result<Success> {
+ return Error() << getpid() << " " << getppid();
+ });
+
+ // For SetProp
+ test_function_map.Add("setprop", 2, 2, true, [](const BuiltinArguments& args) {
+ android::base::SetProperty(args[1], args[2]);
+ return Success();
+ });
+
+ // For MultipleCommands
+ // Using a shared_ptr to extend lifetime of words to both lambdas
+ auto words = std::make_shared<std::vector<std::string>>();
+ test_function_map.Add("add_word", 1, 1, true, [words](const BuiltinArguments& args) {
+ words->emplace_back(args[1]);
+ return Success();
+ });
+ test_function_map.Add("return_words_as_error", 0, 0, true,
+ [words](const BuiltinArguments& args) -> Result<Success> {
+ return Error() << Join(*words, " ");
+ });
+
+ // For RecoverAfterAbort
+ test_function_map.Add("cause_log_fatal", 0, 0, true,
+ [](const BuiltinArguments& args) -> Result<Success> {
+ return Error() << std::string(4097, 'f');
+ });
+ test_function_map.Add(
+ "generate_sane_error", 0, 0, true,
+ [](const BuiltinArguments& args) -> Result<Success> { return Error() << "Sane error!"; });
+
+ // For ContextString
+ test_function_map.Add(
+ "return_context_as_error", 0, 0, true,
+ [](const BuiltinArguments& args) -> Result<Success> { return Error() << args.context; });
+
+ return test_function_map;
+}
+
+} // namespace init
+} // namespace android
+
+int main(int argc, char** argv) {
+ if (argc > 1 && !strcmp(basename(argv[1]), "subcontext")) {
+ auto test_function_map = android::init::BuildTestFunctionMap();
+ return android::init::SubcontextMain(argc, argv, &test_function_map);
+ }
+
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/init/test_function_map.h b/init/test_function_map.h
new file mode 100644
index 0000000..583df1a
--- /dev/null
+++ b/init/test_function_map.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2017 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_TEST_FUNCTION_MAP_H
+#define _INIT_TEST_FUNCTION_MAP_H
+
+#include <string>
+#include <vector>
+
+#include "builtin_arguments.h"
+#include "keyword_map.h"
+
+namespace android {
+namespace init {
+
+class TestFunctionMap : public KeywordFunctionMap {
+ public:
+ // Helper for argument-less functions
+ using BuiltinFunctionNoArgs = std::function<void(void)>;
+ void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
+ Add(name, 0, 0, false, [function](const BuiltinArguments&) {
+ function();
+ return Success();
+ });
+ }
+
+ void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
+ bool run_in_subcontext, const BuiltinFunction function) {
+ builtin_functions_[name] =
+ make_tuple(min_parameters, max_parameters, make_pair(run_in_subcontext, function));
+ }
+
+ private:
+ Map builtin_functions_ = {};
+
+ const Map& map() const override { return builtin_functions_; }
+};
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index e889bdd..2f20684 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -83,7 +83,7 @@
darwin: {
enabled: true,
},
- linux: {
+ linux_glibc: {
srcs: libbacktrace_sources,
shared_libs: [
@@ -94,7 +94,6 @@
],
static_libs: ["libcutils"],
- host_ldlibs: ["-lrt"],
},
linux_bionic: {
enabled: true,
@@ -136,7 +135,7 @@
srcs: ["backtrace_testlib.cpp"],
target: {
- linux: {
+ linux_glibc: {
shared_libs: [
"libunwind",
"libunwindstack",
@@ -229,15 +228,11 @@
android: {
cflags: ["-DENABLE_PSS_TESTS"],
shared_libs: [
- "libdl",
"libutils",
],
},
- linux: {
+ linux_glibc: {
host_ldlibs: [
- "-lpthread",
- "-lrt",
- "-ldl",
"-lncurses",
],
static_libs: ["libutils"],
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index b4481cc..41153ce 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -22,6 +22,7 @@
#include <ucontext.h>
#include <memory>
+#include <set>
#include <string>
#if !defined(__ANDROID__)
@@ -37,6 +38,8 @@
#include <unwindstack/Regs.h>
#include <unwindstack/RegsGetLocal.h>
+#include <unwindstack/Unwinder.h>
+
#include "BacktraceLog.h"
#include "UnwindStack.h"
#include "UnwindStackMap.h"
@@ -63,135 +66,42 @@
return name;
}
-static bool IsUnwindLibrary(const std::string& map_name) {
- const std::string library(basename(map_name.c_str()));
- return library == "libunwindstack.so" || library == "libbacktrace.so";
-}
-
-static void SetFrameInfo(unwindstack::Regs* regs, unwindstack::MapInfo* map_info,
- uint64_t adjusted_rel_pc, backtrace_frame_data_t* frame) {
- // This will point to the adjusted absolute pc. regs->pc() is
- // unaltered.
- frame->pc = map_info->start + adjusted_rel_pc;
- frame->sp = regs->sp();
- frame->rel_pc = adjusted_rel_pc;
- frame->stack_size = 0;
-
- frame->map.start = map_info->start;
- frame->map.end = map_info->end;
- frame->map.offset = map_info->offset;
- frame->map.flags = map_info->flags;
- frame->map.name = map_info->name;
-
- unwindstack::Elf* elf = map_info->elf;
- frame->map.load_bias = elf->GetLoadBias();
- uint64_t func_offset = 0;
- if (elf->GetFunctionName(adjusted_rel_pc, &frame->func_name, &func_offset)) {
- frame->func_name = demangle(frame->func_name.c_str());
- } else {
- frame->func_name = "";
- }
- frame->func_offset = func_offset;
-}
-
static bool Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames) {
+ static std::set<std::string> skip_names{"libunwindstack.so", "libbacktrace.so"};
UnwindStackMap* stack_map = reinterpret_cast<UnwindStackMap*>(back_map);
- unwindstack::Maps* maps = stack_map->stack_maps();
- bool adjust_rel_pc = false;
- size_t num_frames = 0;
- frames->clear();
- bool return_address_attempted = false;
auto process_memory = stack_map->process_memory();
- while (num_frames < MAX_BACKTRACE_FRAMES) {
- unwindstack::MapInfo* map_info = maps->Find(regs->pc());
- bool stepped;
- bool in_device_map = false;
- if (map_info == nullptr) {
- stepped = false;
- if (num_ignore_frames == 0) {
- frames->resize(num_frames + 1);
- backtrace_frame_data_t* frame = &frames->at(num_frames);
- frame->pc = regs->pc();
- frame->sp = regs->sp();
- frame->rel_pc = frame->pc;
- num_frames++;
- } else {
- num_ignore_frames--;
- }
- } else {
- unwindstack::Elf* elf = map_info->GetElf(process_memory, true);
- uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
+ unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(),
+ regs, stack_map->process_memory());
+ unwinder.Unwind(&skip_names);
- if (frames->size() != 0 || !IsUnwindLibrary(map_info->name)) {
- if (num_ignore_frames == 0) {
- uint64_t adjusted_rel_pc = rel_pc;
- if (adjust_rel_pc) {
- adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
- }
+ if (num_ignore_frames >= unwinder.NumFrames()) {
+ frames->resize(0);
+ return true;
+ }
- frames->resize(num_frames + 1);
- backtrace_frame_data_t* frame = &frames->at(num_frames);
- frame->num = num_frames;
- SetFrameInfo(regs, map_info, adjusted_rel_pc, frame);
+ frames->resize(unwinder.NumFrames() - num_ignore_frames);
+ auto unwinder_frames = unwinder.frames();
+ size_t cur_frame = 0;
+ for (size_t i = num_ignore_frames; i < unwinder.NumFrames(); i++, cur_frame++) {
+ auto frame = &unwinder_frames[i];
+ backtrace_frame_data_t* back_frame = &frames->at(cur_frame);
- if (num_frames > 0) {
- // Set the stack size for the previous frame.
- backtrace_frame_data_t* prev = &frames->at(num_frames - 1);
- prev->stack_size = frame->sp - prev->sp;
- }
- num_frames++;
- } else {
- num_ignore_frames--;
- }
- }
+ back_frame->num = frame->num;
- if (map_info->flags & PROT_DEVICE_MAP) {
- // Do not stop here, fall through in case we are
- // in the speculative unwind path and need to remove
- // some of the speculative frames.
- stepped = false;
- in_device_map = true;
- } else {
- unwindstack::MapInfo* sp_info = maps->Find(regs->sp());
- if (sp_info->flags & PROT_DEVICE_MAP) {
- // Do not stop here, fall through in case we are
- // in the speculative unwind path and need to remove
- // some of the speculative frames.
- stepped = false;
- in_device_map = true;
- } else {
- bool finished;
- stepped = elf->Step(rel_pc + map_info->elf_offset, regs, process_memory.get(), &finished);
- if (stepped && finished) {
- break;
- }
- }
- }
- }
- adjust_rel_pc = true;
+ back_frame->rel_pc = frame->rel_pc;
+ back_frame->pc = frame->pc;
+ back_frame->sp = frame->sp;
- if (!stepped) {
- if (return_address_attempted) {
- // Remove the speculative frame.
- if (frames->size() > 0) {
- frames->pop_back();
- }
- break;
- } else if (in_device_map) {
- // Do not attempt any other unwinding, pc or sp is in a device
- // map.
- break;
- } else {
- // Stepping didn't work, try this secondary method.
- if (!regs->SetPcFromReturnAddress(process_memory.get())) {
- break;
- }
- return_address_attempted = true;
- }
- } else {
- return_address_attempted = false;
- }
+ back_frame->func_name = frame->function_name;
+ back_frame->func_offset = frame->function_offset;
+
+ back_frame->map.name = frame->map_name;
+ back_frame->map.start = frame->map_start;
+ back_frame->map.end = frame->map_end;
+ back_frame->map.offset = frame->map_offset;
+ back_frame->map.load_bias = frame->map_load_bias;
+ back_frame->map.flags = frame->map_flags;
}
return true;
diff --git a/libcutils/ashmem-dev.c b/libcutils/ashmem-dev.c
index b4abb79..95f2259 100644
--- a/libcutils/ashmem-dev.c
+++ b/libcutils/ashmem-dev.c
@@ -51,7 +51,7 @@
int ret;
struct stat st;
- int fd = TEMP_FAILURE_RETRY(open(ASHMEM_DEVICE, O_RDWR));
+ int fd = TEMP_FAILURE_RETRY(open(ASHMEM_DEVICE, O_RDWR | O_CLOEXEC));
if (fd < 0) {
return fd;
}
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp
index 23a5c79..b92f086 100644
--- a/libdiskconfig/Android.bp
+++ b/libdiskconfig/Android.bp
@@ -23,7 +23,7 @@
darwin: {
enabled: false,
},
- linux: {
+ linux_glibc: {
cflags: [
"-O2",
"-g",
diff --git a/liblog/Android.bp b/liblog/Android.bp
index b98d18f..d5bb29e 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -91,9 +91,6 @@
not_windows: {
srcs: ["event_tag_map.cpp"],
},
- linux: {
- host_ldlibs: ["-lrt"],
- },
linux_bionic: {
enabled: true,
},
diff --git a/liblog/include/log/log_safetynet.h b/liblog/include/log/log_safetynet.h
index b8ca475..07e8c8a 100644
--- a/liblog/include/log/log_safetynet.h
+++ b/liblog/include/log/log_safetynet.h
@@ -10,6 +10,8 @@
#ifndef _LIBS_LOG_SAFETYNET_H
#define _LIBS_LOG_SAFETYNET_H
+#include <stdint.h>
+
#ifdef __cplusplus
extern "C" {
#endif
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
index fcabcc9..189bc4b 100644
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -42,8 +42,8 @@
LOGBUILDER_COUNTER = 803,
LOGBUILDER_HISTOGRAM = 804,
- ACTION_BOOT = 1092,
- FIELD_PLATFORM_REASON = 1093,
+ ACTION_BOOT = 1098,
+ FIELD_PLATFORM_REASON = 1099,
};
enum {
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index b3c42f0..089f3b8 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -22,13 +22,6 @@
cppflags: [
"-fvisibility=protected",
],
-
- host_ldlibs: ["-ldl"],
- target: {
- android: {
- shared_libs: ["libdl"],
- },
- },
}
subdirs = ["tests"]
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
index e31dae0..9e2e641 100644
--- a/libnativebridge/tests/Android.bp
+++ b/libnativebridge/tests/Android.bp
@@ -25,14 +25,6 @@
],
header_libs: ["libnativebridge-dummy-headers"],
cppflags: ["-fvisibility=protected"],
- target: {
- android: {
- shared_libs: ["libdl"],
- },
- host: {
- host_ldlibs: ["-ldl"],
- },
- },
}
cc_library_shared {
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index 13f9744..4b21edc 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -11,14 +11,6 @@
"libnativebridge",
"libbase",
],
- target: {
- android: {
- shared_libs: ["libdl"],
- },
- host: {
- host_ldlibs: ["-ldl"],
- },
- },
cflags: [
"-Werror",
"-Wall",
@@ -27,5 +19,4 @@
"-fvisibility=hidden",
],
export_include_dirs: ["include"],
- local_include_dirs: ["include"],
}
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 6ec0991..b894656 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -15,19 +15,11 @@
cflags: ["-Werror"],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
+ shared_libs: [
+ "libz",
+ "libbase",
+ ],
target: {
- host: {
- shared_libs: [
- "libz-host",
- "libbase",
- ],
- },
- android: {
- shared_libs: [
- "libz",
- "libbase",
- ],
- },
windows: {
enabled: true,
},
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index d076a1a..3a12292 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -23,7 +23,6 @@
"libbase",
"libcutils",
"liblog",
- "libnl",
],
export_include_dirs: ["include"],
diff --git a/libsysutils/include/sysutils/NetlinkEvent.h b/libsysutils/include/sysutils/NetlinkEvent.h
index b80f3ea..f9fc11b 100644
--- a/libsysutils/include/sysutils/NetlinkEvent.h
+++ b/libsysutils/include/sysutils/NetlinkEvent.h
@@ -64,6 +64,7 @@
bool parseNfPacketMessage(struct nlmsghdr *nh);
bool parseRtMessage(const struct nlmsghdr *nh);
bool parseNdUserOptMessage(const struct nlmsghdr *nh);
+ struct nlattr* findNlAttr(const nlmsghdr* nl, size_t hdrlen, uint16_t attr);
};
#endif
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 79bc888..00b1ee2 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "NetlinkEvent"
#include <arpa/inet.h>
+#include <limits.h>
+#include <linux/genetlink.h>
#include <linux/if.h>
#include <linux/if_addr.h>
#include <linux/if_link.h>
@@ -26,12 +28,8 @@
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
-#include <netinet/in.h>
#include <netinet/icmp6.h>
-#include <netlink/attr.h>
-#include <netlink/genl/genl.h>
-#include <netlink/handlers.h>
-#include <netlink/msg.h>
+#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
@@ -263,6 +261,18 @@
return true;
}
+static size_t nlAttrLen(const nlattr* nla) {
+ return nla->nla_len - NLA_HDRLEN;
+}
+
+static const uint8_t* nlAttrData(const nlattr* nla) {
+ return reinterpret_cast<const uint8_t*>(nla) + NLA_HDRLEN;
+}
+
+static uint32_t nlAttrU32(const nlattr* nla) {
+ return *reinterpret_cast<const uint32_t*>(nlAttrData(nla));
+}
+
/*
* Parse a LOCAL_NFLOG_PACKET message.
*/
@@ -271,17 +281,17 @@
int len = 0;
char* raw = NULL;
- struct nlattr *uid_attr = nlmsg_find_attr(nh, sizeof(struct genlmsghdr), NFULA_UID);
+ struct nlattr* uid_attr = findNlAttr(nh, sizeof(struct genlmsghdr), NFULA_UID);
if (uid_attr) {
- uid = ntohl(nla_get_u32(uid_attr));
+ uid = ntohl(nlAttrU32(uid_attr));
}
- struct nlattr *payload = nlmsg_find_attr(nh, sizeof(struct genlmsghdr), NFULA_PAYLOAD);
+ struct nlattr* payload = findNlAttr(nh, sizeof(struct genlmsghdr), NFULA_PAYLOAD);
if (payload) {
/* First 256 bytes is plenty */
- len = nla_len(payload);
+ len = nlAttrLen(payload);
if (len > 256) len = 256;
- raw = (char*) nla_data(payload);
+ raw = (char*)nlAttrData(payload);
}
char* hex = (char*) calloc(1, 5 + (len * 2));
@@ -646,3 +656,26 @@
SLOGE("NetlinkEvent::FindParam(): Parameter '%s' not found", paramName);
return NULL;
}
+
+nlattr* NetlinkEvent::findNlAttr(const nlmsghdr* nh, size_t hdrlen, uint16_t attr) {
+ if (nh == nullptr || NLMSG_HDRLEN + NLMSG_ALIGN(hdrlen) > SSIZE_MAX) {
+ return nullptr;
+ }
+
+ // Skip header, padding, and family header.
+ const ssize_t NLA_START = NLMSG_HDRLEN + NLMSG_ALIGN(hdrlen);
+ ssize_t left = nh->nlmsg_len - NLA_START;
+ uint8_t* hdr = ((uint8_t*)nh) + NLA_START;
+
+ while (left >= NLA_HDRLEN) {
+ nlattr* nla = (nlattr*)hdr;
+ if (nla->nla_type == attr) {
+ return nla;
+ }
+
+ hdr += NLA_ALIGN(nla->nla_len);
+ left -= NLA_ALIGN(nla->nla_len);
+ }
+
+ return nullptr;
+}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index ca05516..f40086e 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -67,7 +67,7 @@
target: {
// Always disable optimizations for host to make it easier to debug.
- linux: {
+ host: {
cflags: ["-O0", "-g"],
},
},
@@ -107,6 +107,7 @@
"tests/DwarfOpTest.cpp",
"tests/DwarfSectionTest.cpp",
"tests/DwarfSectionImplTest.cpp",
+ "tests/ElfFake.cpp",
"tests/ElfInterfaceArmTest.cpp",
"tests/ElfInterfaceTest.cpp",
"tests/ElfTest.cpp",
@@ -125,6 +126,7 @@
"tests/RegsTest.cpp",
"tests/SymbolsTest.cpp",
"tests/UnwindTest.cpp",
+ "tests/UnwinderTest.cpp",
],
cflags: [
@@ -143,14 +145,6 @@
"libgmock",
],
- target: {
- linux: {
- host_ldlibs: [
- "-lrt",
- ],
- },
- },
-
data: [
"tests/files/elf32.xz",
"tests/files/elf64.xz",
@@ -178,14 +172,6 @@
srcs: [
"tools/unwind.cpp",
],
-
- target: {
- linux: {
- host_ldlibs: [
- "-lrt",
- ],
- },
- },
}
cc_binary {
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index 8c6172e..69e6512 100644
--- a/libunwindstack/Regs.cpp
+++ b/libunwindstack/Regs.cpp
@@ -33,26 +33,6 @@
namespace unwindstack {
-template <typename AddressType>
-bool RegsImpl<AddressType>::GetReturnAddressFromDefault(Memory* memory, uint64_t* value) {
- switch (return_loc_.type) {
- case LOCATION_REGISTER:
- CHECK(return_loc_.value < total_regs_);
- *value = regs_[return_loc_.value];
- return true;
- case LOCATION_SP_OFFSET:
- AddressType return_value;
- if (!memory->Read(sp_ + return_loc_.value, &return_value, sizeof(return_value))) {
- return false;
- }
- *value = return_value;
- return true;
- case LOCATION_UNKNOWN:
- default:
- return false;
- }
-}
-
RegsArm::RegsArm()
: RegsImpl<uint32_t>(ARM_REG_LAST, ARM_REG_SP, Location(LOCATION_REGISTER, ARM_REG_LR)) {}
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 1a86e16..e648927 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -14,9 +14,11 @@
* limitations under the License.
*/
+#define _GNU_SOURCE 1
#include <elf.h>
#include <inttypes.h>
#include <stdint.h>
+#include <string.h>
#include <sys/types.h>
#include <unistd.h>
@@ -28,35 +30,33 @@
namespace unwindstack {
-void Unwinder::FillInFrame(MapInfo* map_info, uint64_t* rel_pc) {
+void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, bool adjust_pc) {
size_t frame_num = frames_.size();
frames_.resize(frame_num + 1);
FrameData* frame = &frames_.at(frame_num);
frame->num = frame_num;
frame->pc = regs_->pc();
frame->sp = regs_->sp();
- frame->rel_pc = frame->pc;
+ frame->rel_pc = rel_pc;
if (map_info == nullptr) {
return;
}
- Elf* elf = map_info->GetElf(process_memory_, true);
- *rel_pc = elf->GetRelPc(regs_->pc(), map_info);
- if (frame_num != 0) {
+ if (adjust_pc) {
// Don't adjust the first frame pc.
- frame->rel_pc = regs_->GetAdjustedPc(*rel_pc, elf);
+ frame->rel_pc = regs_->GetAdjustedPc(rel_pc, elf);
// Adjust the original pc.
- frame->pc -= *rel_pc - frame->rel_pc;
- } else {
- frame->rel_pc = *rel_pc;
+ frame->pc -= rel_pc - frame->rel_pc;
}
frame->map_name = map_info->name;
frame->map_offset = map_info->elf_offset;
frame->map_start = map_info->start;
frame->map_end = map_info->end;
+ frame->map_flags = map_info->flags;
+ frame->map_load_bias = elf->GetLoadBias();
if (!elf->GetFunctionName(frame->rel_pc, &frame->function_name, &frame->function_offset)) {
frame->function_name = "";
@@ -64,25 +64,59 @@
}
}
-void Unwinder::Unwind() {
+void Unwinder::Unwind(std::set<std::string>* initial_map_names_to_skip) {
frames_.clear();
bool return_address_attempt = false;
+ bool adjust_pc = false;
for (; frames_.size() < max_frames_;) {
MapInfo* map_info = maps_->Find(regs_->pc());
uint64_t rel_pc;
- FillInFrame(map_info, &rel_pc);
+ Elf* elf;
+ if (map_info == nullptr) {
+ rel_pc = regs_->pc();
+ } else {
+ elf = map_info->GetElf(process_memory_, true);
+ rel_pc = elf->GetRelPc(regs_->pc(), map_info);
+ }
+
+ if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
+ initial_map_names_to_skip->find(basename(map_info->name.c_str())) ==
+ initial_map_names_to_skip->end()) {
+ FillInFrame(map_info, elf, rel_pc, adjust_pc);
+ // Once a frame is added, stop skipping frames.
+ initial_map_names_to_skip = nullptr;
+ }
+ adjust_pc = true;
bool stepped;
+ bool in_device_map = false;
if (map_info == nullptr) {
stepped = false;
} else {
- bool finished;
- stepped = map_info->elf->Step(rel_pc + map_info->elf_offset, regs_, process_memory_.get(),
- &finished);
- if (stepped && finished) {
- break;
+ if (map_info->flags & MAPS_FLAGS_DEVICE_MAP) {
+ // Do not stop here, fall through in case we are
+ // in the speculative unwind path and need to remove
+ // some of the speculative frames.
+ stepped = false;
+ in_device_map = true;
+ } else {
+ MapInfo* sp_info = maps_->Find(regs_->sp());
+ if (sp_info != nullptr && sp_info->flags & MAPS_FLAGS_DEVICE_MAP) {
+ // Do not stop here, fall through in case we are
+ // in the speculative unwind path and need to remove
+ // some of the speculative frames.
+ stepped = false;
+ in_device_map = true;
+ } else {
+ bool finished;
+ stepped =
+ elf->Step(rel_pc + map_info->elf_offset, regs_, process_memory_.get(), &finished);
+ if (stepped && finished) {
+ break;
+ }
+ }
}
}
if (!stepped) {
@@ -90,6 +124,10 @@
// Remove the speculative frame.
frames_.pop_back();
break;
+ } else if (in_device_map) {
+ // Do not attempt any other unwinding, pc or sp is in a device
+ // map.
+ break;
} else {
// Steping didn't work, try this secondary method.
if (!regs_->SetPcFromReturnAddress(process_memory_.get())) {
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index 385ee18..9d3150b 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -55,8 +55,6 @@
virtual uint64_t pc() = 0;
virtual uint64_t sp() = 0;
- virtual bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) = 0;
-
virtual uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) = 0;
virtual bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) = 0;
@@ -86,8 +84,6 @@
: Regs(total_regs, sp_reg, return_loc), regs_(total_regs) {}
virtual ~RegsImpl() = default;
- bool GetReturnAddressFromDefault(Memory* memory, uint64_t* value) override;
-
uint64_t pc() override { return pc_; }
uint64_t sp() override { return sp_; }
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index d66fe55..71703b4 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include <memory>
+#include <set>
#include <string>
#include <vector>
@@ -30,6 +31,9 @@
namespace unwindstack {
+// Forward declarations.
+class Elf;
+
struct FrameData {
size_t num;
@@ -44,6 +48,8 @@
uint64_t map_offset;
uint64_t map_start;
uint64_t map_end;
+ uint64_t map_load_bias;
+ int map_flags;
};
class Unwinder {
@@ -54,7 +60,7 @@
}
~Unwinder() = default;
- void Unwind();
+ void Unwind(std::set<std::string>* initial_map_names_to_skip = nullptr);
size_t NumFrames() { return frames_.size(); }
@@ -64,7 +70,7 @@
static std::string FormatFrame(const FrameData& frame, bool bits32);
private:
- void FillInFrame(MapInfo* map_info, uint64_t* rel_pc);
+ void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, bool adjust_pc);
size_t max_frames_;
Maps* maps_;
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index 69813e5..90baabe 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -25,7 +25,6 @@
#include "LogFake.h"
#include "MemoryFake.h"
-#include "RegsFake.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp
index 07159b0..21114da 100644
--- a/libunwindstack/tests/DwarfEhFrameTest.cpp
+++ b/libunwindstack/tests/DwarfEhFrameTest.cpp
@@ -25,7 +25,6 @@
#include "LogFake.h"
#include "MemoryFake.h"
-#include "RegsFake.h"
namespace unwindstack {
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
index 47a40cf..2d5007b 100644
--- a/libunwindstack/tests/DwarfOpTest.cpp
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -1486,7 +1486,7 @@
}
this->op_memory_.SetMemory(0, opcode_buffer);
- RegsFake<TypeParam> regs(32, 10);
+ RegsImplFake<TypeParam> regs(32, 10);
for (size_t i = 0; i < 32; i++) {
regs[i] = i + 10;
}
@@ -1518,7 +1518,7 @@
};
this->op_memory_.SetMemory(0, opcode_buffer);
- RegsFake<TypeParam> regs(16, 10);
+ RegsImplFake<TypeParam> regs(16, 10);
for (size_t i = 0; i < 16; i++) {
regs[i] = i + 10;
}
@@ -1544,7 +1544,7 @@
0x92, 0x80, 0x15, 0x80, 0x02};
this->op_memory_.SetMemory(0, opcode_buffer);
- RegsFake<TypeParam> regs(10, 10);
+ RegsImplFake<TypeParam> regs(10, 10);
regs[5] = 0x45;
regs[6] = 0x190;
this->op_->set_regs(®s);
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index 2939126..c701a29 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -90,7 +90,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_eval_fail) {
DwarfCie cie{.version = 3, .return_address_register = 5};
- RegsFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10, 9);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -105,7 +105,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_no_stack) {
DwarfCie cie{.version = 3, .return_address_register = 5};
- RegsFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10, 9);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -121,7 +121,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr) {
DwarfCie cie{.version = 3, .return_address_register = 5};
- RegsFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10, 9);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -141,7 +141,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_val_expr) {
DwarfCie cie{.version = 3, .return_address_register = 5};
- RegsFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10, 9);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -159,7 +159,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_is_register) {
DwarfCie cie{.version = 3, .return_address_register = 5};
- RegsFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10, 9);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -175,7 +175,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_bad_regs) {
DwarfCie cie{.return_address_register = 60};
- RegsFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10, 9);
dwarf_loc_regs_t loc_regs;
bool finished;
@@ -185,7 +185,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_no_cfa) {
DwarfCie cie{.return_address_register = 5};
- RegsFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10, 9);
dwarf_loc_regs_t loc_regs;
bool finished;
@@ -195,7 +195,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_bad) {
DwarfCie cie{.return_address_register = 5};
- RegsFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10, 9);
dwarf_loc_regs_t loc_regs;
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {20, 0}};
@@ -224,7 +224,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_prev) {
DwarfCie cie{.return_address_register = 5};
- RegsFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10, 9);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -241,7 +241,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_from_value) {
DwarfCie cie{.return_address_register = 5};
- RegsFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10, 9);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -259,7 +259,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_double_indirection) {
DwarfCie cie{.return_address_register = 5};
- RegsFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10, 9);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -275,7 +275,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_invalid_register) {
DwarfCie cie{.return_address_register = 5};
- RegsFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10, 9);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -290,7 +290,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_different_reg_locations) {
DwarfCie cie{.return_address_register = 5};
- RegsFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10, 9);
dwarf_loc_regs_t loc_regs;
if (sizeof(TypeParam) == sizeof(uint64_t)) {
@@ -324,7 +324,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address_undefined) {
DwarfCie cie{.return_address_register = 5};
- RegsFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10, 9);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -342,7 +342,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address) {
DwarfCie cie{.return_address_register = 5};
- RegsFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10, 9);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -359,7 +359,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_ignore_large_reg_loc) {
DwarfCie cie{.return_address_register = 5};
- RegsFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10, 9);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -378,7 +378,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_expr) {
DwarfCie cie{.version = 3, .return_address_register = 5};
- RegsFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10, 9);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -398,7 +398,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_val_expr) {
DwarfCie cie{.version = 3, .return_address_register = 5};
- RegsFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10, 9);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -416,7 +416,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_same_cfa_same_pc) {
DwarfCie cie{.version = 3, .return_address_register = 5};
- RegsFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10, 9);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
new file mode 100644
index 0000000..71f7f6b
--- /dev/null
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <deque>
+#include <string>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+#include "ElfFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+std::deque<FunctionData> ElfInterfaceFake::functions_;
+std::deque<StepData> ElfInterfaceFake::steps_;
+
+bool ElfInterfaceFake::GetFunctionName(uint64_t, std::string* name, uint64_t* offset) {
+ if (functions_.empty()) {
+ return false;
+ }
+ auto entry = functions_.front();
+ functions_.pop_front();
+ *name = entry.name;
+ *offset = entry.offset;
+ return true;
+}
+
+bool ElfInterfaceFake::Step(uint64_t, Regs* regs, Memory*, bool* finished) {
+ if (steps_.empty()) {
+ return false;
+ }
+ auto entry = steps_.front();
+ steps_.pop_front();
+
+ if (entry.pc == 0 && entry.sp == 0 && !entry.finished) {
+ // Pretend as though there is no frame.
+ return false;
+ }
+
+ RegsFake* fake_regs = reinterpret_cast<RegsFake*>(regs);
+ fake_regs->FakeSetPc(entry.pc);
+ fake_regs->FakeSetSp(entry.sp);
+ *finished = entry.finished;
+ return true;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
new file mode 100644
index 0000000..4359bca
--- /dev/null
+++ b/libunwindstack/tests/ElfFake.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
+#define _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
+
+#include <stdint.h>
+
+#include <deque>
+#include <string>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/ElfInterface.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+struct StepData {
+ StepData(uint64_t pc, uint64_t sp, bool finished) : pc(pc), sp(sp), finished(finished) {}
+ uint64_t pc;
+ uint64_t sp;
+ bool finished;
+};
+
+struct FunctionData {
+ FunctionData(std::string name, uint64_t offset) : name(name), offset(offset) {}
+
+ std::string name;
+ uint64_t offset;
+};
+
+class ElfFake : public Elf {
+ public:
+ ElfFake(Memory* memory) : Elf(memory) { valid_ = true; }
+ virtual ~ElfFake() = default;
+
+ void FakeSetInterface(ElfInterface* interface) { interface_.reset(interface); }
+};
+
+class ElfInterfaceFake : public ElfInterface {
+ public:
+ ElfInterfaceFake(Memory* memory) : ElfInterface(memory) {}
+ virtual ~ElfInterfaceFake() = default;
+
+ bool Init() override { return false; }
+ void InitHeaders() override {}
+ bool GetSoname(std::string*) override { return false; }
+
+ bool GetFunctionName(uint64_t, std::string*, uint64_t*) override;
+
+ bool Step(uint64_t, Regs*, Memory*, bool*) override;
+
+ void FakeSetLoadBias(uint64_t load_bias) { load_bias_ = load_bias; }
+
+ static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
+ static void FakePushStepData(const StepData data) { steps_.push_back(data); }
+
+ static void FakeClear() {
+ functions_.clear();
+ steps_.clear();
+ }
+
+ private:
+ static std::deque<FunctionData> functions_;
+ static std::deque<StepData> steps_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_TESTS_ELF_FAKE_H
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index b667ec1..efcd029 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -24,20 +24,60 @@
namespace unwindstack {
-template <typename TypeParam>
-class RegsFake : public RegsImpl<TypeParam> {
+class RegsFake : public Regs {
public:
RegsFake(uint16_t total_regs, uint16_t sp_reg)
- : RegsImpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+ : Regs(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
virtual ~RegsFake() = default;
+ uint32_t MachineType() override { return fake_type_; }
+ void* RawData() override { return nullptr; }
+ uint64_t pc() override { return fake_pc_; }
+ uint64_t sp() override { return fake_sp_; }
+ bool SetPcFromReturnAddress(Memory*) override {
+ if (!fake_return_address_valid_) {
+ return false;
+ }
+ fake_pc_ = fake_return_address_;
+ return true;
+ }
+
+ uint64_t GetAdjustedPc(uint64_t rel_pc, Elf*) override { return rel_pc - 2; }
+
+ bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
+
+ void SetFromRaw() override {}
+
+ void FakeSetMachineType(uint32_t type) { fake_type_ = type; }
+ void FakeSetPc(uint64_t pc) { fake_pc_ = pc; }
+ void FakeSetSp(uint64_t sp) { fake_sp_ = sp; }
+ void FakeSetReturnAddress(uint64_t return_address) { fake_return_address_ = return_address; }
+ void FakeSetReturnAddressValid(bool valid) { fake_return_address_valid_ = valid; }
+
+ private:
+ uint32_t fake_type_ = 0;
+ uint64_t fake_pc_ = 0;
+ uint64_t fake_sp_ = 0;
+ bool fake_return_address_valid_ = false;
+ uint64_t fake_return_address_ = 0;
+};
+
+template <typename TypeParam>
+class RegsImplFake : public RegsImpl<TypeParam> {
+ public:
+ RegsImplFake(uint16_t total_regs, uint16_t sp_reg)
+ : RegsImpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+ virtual ~RegsImplFake() = default;
+
uint32_t MachineType() override { return 0; }
uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
void SetFromRaw() override {}
bool SetPcFromReturnAddress(Memory*) override { return false; }
bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
- bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
+
+ void FakeSetPc(uint64_t pc) { this->pc_ = pc; }
+ void FakeSetSp(uint64_t sp) { this->sp_ = sp; }
};
} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 3912e17..3b9f92b 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -23,68 +23,28 @@
#include <unwindstack/MapInfo.h>
#include <unwindstack/Regs.h>
+#include "ElfFake.h"
#include "MemoryFake.h"
+#include "RegsFake.h"
namespace unwindstack {
-class ElfFake : public Elf {
- public:
- ElfFake(Memory* memory) : Elf(memory) { valid_ = true; }
- virtual ~ElfFake() = default;
-
- void set_elf_interface(ElfInterface* interface) { interface_.reset(interface); }
-};
-
-class ElfInterfaceFake : public ElfInterface {
- public:
- ElfInterfaceFake(Memory* memory) : ElfInterface(memory) {}
- virtual ~ElfInterfaceFake() = default;
-
- void set_load_bias(uint64_t load_bias) { load_bias_ = load_bias; }
-
- bool Init() override { return false; }
- void InitHeaders() override {}
- bool GetSoname(std::string*) override { return false; }
- bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
- bool Step(uint64_t, Regs*, Memory*, bool*) override { return false; }
-};
-
-template <typename TypeParam>
-class RegsTestImpl : public RegsImpl<TypeParam> {
- public:
- RegsTestImpl(uint16_t total_regs, uint16_t regs_sp)
- : RegsImpl<TypeParam>(total_regs, regs_sp, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
- RegsTestImpl(uint16_t total_regs, uint16_t regs_sp, Regs::Location return_loc)
- : RegsImpl<TypeParam>(total_regs, regs_sp, return_loc) {}
- virtual ~RegsTestImpl() = default;
-
- uint32_t MachineType() override { return 0; }
-
- uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
- void SetFromRaw() override {}
- bool SetPcFromReturnAddress(Memory*) override { return false; }
- bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
-};
-
class RegsTest : public ::testing::Test {
protected:
void SetUp() override {
memory_ = new MemoryFake;
elf_.reset(new ElfFake(memory_));
elf_interface_ = new ElfInterfaceFake(elf_->memory());
- elf_->set_elf_interface(elf_interface_);
+ elf_->FakeSetInterface(elf_interface_);
}
- template <typename AddressType>
- void RegsReturnAddressRegister();
-
ElfInterfaceFake* elf_interface_;
MemoryFake* memory_;
std::unique_ptr<ElfFake> elf_;
};
TEST_F(RegsTest, regs32) {
- RegsTestImpl<uint32_t> regs32(50, 10);
+ RegsImplFake<uint32_t> regs32(50, 10);
ASSERT_EQ(50U, regs32.total_regs());
ASSERT_EQ(10U, regs32.sp_reg());
@@ -107,7 +67,7 @@
}
TEST_F(RegsTest, regs64) {
- RegsTestImpl<uint64_t> regs64(30, 12);
+ RegsImplFake<uint64_t> regs64(30, 12);
ASSERT_EQ(30U, regs64.total_regs());
ASSERT_EQ(12U, regs64.sp_reg());
@@ -129,44 +89,6 @@
ASSERT_EQ(10U, regs64[8]);
}
-template <typename AddressType>
-void RegsTest::RegsReturnAddressRegister() {
- RegsTestImpl<AddressType> regs(20, 10, Regs::Location(Regs::LOCATION_REGISTER, 5));
-
- regs[5] = 0x12345;
- uint64_t value;
- ASSERT_TRUE(regs.GetReturnAddressFromDefault(memory_, &value));
- ASSERT_EQ(0x12345U, value);
-}
-
-TEST_F(RegsTest, regs32_return_address_register) {
- RegsReturnAddressRegister<uint32_t>();
-}
-
-TEST_F(RegsTest, regs64_return_address_register) {
- RegsReturnAddressRegister<uint64_t>();
-}
-
-TEST_F(RegsTest, regs32_return_address_sp_offset) {
- RegsTestImpl<uint32_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -2));
-
- regs.set_sp(0x2002);
- memory_->SetData32(0x2000, 0x12345678);
- uint64_t value;
- ASSERT_TRUE(regs.GetReturnAddressFromDefault(memory_, &value));
- ASSERT_EQ(0x12345678U, value);
-}
-
-TEST_F(RegsTest, regs64_return_address_sp_offset) {
- RegsTestImpl<uint64_t> regs(20, 10, Regs::Location(Regs::LOCATION_SP_OFFSET, -8));
-
- regs.set_sp(0x2008);
- memory_->SetData64(0x2000, 0x12345678aabbccddULL);
- uint64_t value;
- ASSERT_TRUE(regs.GetReturnAddressFromDefault(memory_, &value));
- ASSERT_EQ(0x12345678aabbccddULL, value);
-}
-
TEST_F(RegsTest, rel_pc) {
RegsArm64 arm64;
ASSERT_EQ(0xcU, arm64.GetAdjustedPc(0x10, elf_.get()));
@@ -193,7 +115,7 @@
RegsArm arm;
// Check fence posts.
- elf_interface_->set_load_bias(0);
+ elf_interface_->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()));
@@ -201,7 +123,7 @@
ASSERT_EQ(1U, arm.GetAdjustedPc(0x1, elf_.get()));
ASSERT_EQ(0U, arm.GetAdjustedPc(0x0, elf_.get()));
- elf_interface_->set_load_bias(0x100);
+ elf_interface_->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()));
@@ -211,13 +133,13 @@
ASSERT_EQ(0x100U, arm.GetAdjustedPc(0x100, elf_.get()));
// Check thumb instructions handling.
- elf_interface_->set_load_bias(0);
+ elf_interface_->FakeSetLoadBias(0);
memory_->SetData32(0x2000, 0);
ASSERT_EQ(0x2003U, arm.GetAdjustedPc(0x2005, elf_.get()));
memory_->SetData32(0x2000, 0xe000f000);
ASSERT_EQ(0x2001U, arm.GetAdjustedPc(0x2005, elf_.get()));
- elf_interface_->set_load_bias(0x400);
+ elf_interface_->FakeSetLoadBias(0x400);
memory_->SetData32(0x2100, 0);
ASSERT_EQ(0x2503U, arm.GetAdjustedPc(0x2505, elf_.get()));
memory_->SetData32(0x2100, 0xf111f111);
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
new file mode 100644
index 0000000..4d0366c
--- /dev/null
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -0,0 +1,576 @@
+/*
+ * Copyright (C) 2017 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 <elf.h>
+#include <stdint.h>
+#include <sys/mman.h>
+
+#include <memory>
+#include <set>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
+
+#include "ElfFake.h"
+#include "MemoryFake.h"
+#include "RegsFake.h"
+
+namespace unwindstack {
+
+class MapsFake : public Maps {
+ public:
+ MapsFake() = default;
+ virtual ~MapsFake() = default;
+
+ bool Parse() { return true; }
+
+ void FakeClear() { maps_.clear(); }
+
+ void FakeAddMapInfo(const MapInfo& map_info) { maps_.push_back(map_info); }
+};
+
+class UnwinderTest : public ::testing::Test {
+ protected:
+ static void SetUpTestCase() {
+ maps_.FakeClear();
+ MapInfo info;
+ info.name = "/system/fake/libc.so";
+ info.start = 0x1000;
+ info.end = 0x8000;
+ info.offset = 0;
+ info.flags = PROT_READ | PROT_WRITE;
+ ElfFake* elf = new ElfFake(nullptr);
+ info.elf = elf;
+ elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ info.elf_offset = 0;
+ maps_.FakeAddMapInfo(info);
+
+ info.name = "[stack]";
+ info.start = 0x10000;
+ info.end = 0x12000;
+ info.flags = PROT_READ | PROT_WRITE;
+ info.elf = nullptr;
+ maps_.FakeAddMapInfo(info);
+
+ info.name = "/dev/fake_device";
+ info.start = 0x13000;
+ info.end = 0x15000;
+ info.flags = PROT_READ | PROT_WRITE | MAPS_FLAGS_DEVICE_MAP;
+ info.elf = nullptr;
+ maps_.FakeAddMapInfo(info);
+
+ info.name = "/system/fake/libunwind.so";
+ info.start = 0x20000;
+ info.end = 0x22000;
+ info.flags = PROT_READ | PROT_WRITE;
+ elf = new ElfFake(nullptr);
+ info.elf = elf;
+ elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ maps_.FakeAddMapInfo(info);
+
+ info.name = "/fake/libanother.so";
+ info.start = 0x23000;
+ info.end = 0x24000;
+ info.flags = PROT_READ | PROT_WRITE;
+ elf = new ElfFake(nullptr);
+ info.elf = elf;
+ elf->FakeSetInterface(new ElfInterfaceFake(nullptr));
+ maps_.FakeAddMapInfo(info);
+ }
+
+ void SetUp() override {
+ ElfInterfaceFake::FakeClear();
+ regs_.FakeSetMachineType(EM_ARM);
+ }
+
+ static MapsFake maps_;
+ static RegsFake regs_;
+ static std::shared_ptr<Memory> process_memory_;
+};
+
+MapsFake UnwinderTest::maps_;
+RegsFake UnwinderTest::regs_(5, 0);
+std::shared_ptr<Memory> UnwinderTest::process_memory_(nullptr);
+
+TEST_F(UnwinderTest, multiple_frames) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+ regs_.FakeSetPc(0x1000);
+ regs_.FakeSetSp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ unwinder.Unwind();
+
+ ASSERT_EQ(3U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0x100U, frame->rel_pc);
+ EXPECT_EQ(0x1100U, frame->pc);
+ EXPECT_EQ(0x10010U, frame->sp);
+ EXPECT_EQ("Frame1", frame->function_name);
+ EXPECT_EQ(1U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[2];
+ EXPECT_EQ(2U, frame->num);
+ EXPECT_EQ(0x200U, frame->rel_pc);
+ EXPECT_EQ(0x1200U, frame->pc);
+ EXPECT_EQ(0x10020U, frame->sp);
+ EXPECT_EQ("Frame2", frame->function_name);
+ EXPECT_EQ(2U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify that no attempt to continue after the step indicates it is done.
+TEST_F(UnwinderTest, no_frames_after_finished) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame3", 3));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame4", 4));
+
+ regs_.FakeSetPc(0x1000);
+ regs_.FakeSetSp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0x1000, 0x10000, true));
+ ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
+
+ Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ unwinder.Unwind();
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify the maximum frames to save.
+TEST_F(UnwinderTest, max_frames) {
+ for (size_t i = 0; i < 30; i++) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame" + std::to_string(i), i));
+ ElfInterfaceFake::FakePushStepData(StepData(0x1102 + i * 0x100, 0x10010 + i * 0x10, false));
+ }
+
+ regs_.FakeSetPc(0x1000);
+ regs_.FakeSetSp(0x10000);
+
+ Unwinder unwinder(20, &maps_, ®s_, process_memory_);
+ unwinder.Unwind();
+
+ ASSERT_EQ(20U, unwinder.NumFrames());
+
+ for (size_t i = 0; i < 20; i++) {
+ auto* frame = &unwinder.frames()[i];
+ EXPECT_EQ(i, frame->num);
+ EXPECT_EQ(i * 0x100, frame->rel_pc) << "Failed at frame " << i;
+ EXPECT_EQ(0x1000 + i * 0x100, frame->pc) << "Failed at frame " << i;
+ EXPECT_EQ(0x10000 + 0x10 * i, frame->sp) << "Failed at frame " << i;
+ EXPECT_EQ("Frame" + std::to_string(i), frame->function_name) << "Failed at frame " << i;
+ EXPECT_EQ(i, frame->function_offset) << "Failed at frame " << i;
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name) << "Failed at frame " << i;
+ EXPECT_EQ(0U, frame->map_offset) << "Failed at frame " << i;
+ EXPECT_EQ(0x1000U, frame->map_start) << "Failed at frame " << i;
+ EXPECT_EQ(0x8000U, frame->map_end) << "Failed at frame " << i;
+ EXPECT_EQ(0U, frame->map_load_bias) << "Failed at frame " << i;
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags) << "Failed at frame " << i;
+ }
+}
+
+// Verify that initial map names frames are removed.
+TEST_F(UnwinderTest, verify_frames_skipped) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+ regs_.FakeSetPc(0x20000);
+ regs_.FakeSetSp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x20002, 0x10030, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x10040, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x1002, 0x10050, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x10060, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10070, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ std::set<std::string> skip_set{"libunwind.so", "libanother.so"};
+ unwinder.Unwind(&skip_set);
+
+ ASSERT_EQ(3U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x10050U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0x1000U, frame->rel_pc);
+ EXPECT_EQ(0x21000U, frame->pc);
+ EXPECT_EQ(0x10060U, frame->sp);
+ EXPECT_EQ("Frame1", frame->function_name);
+ EXPECT_EQ(1U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0x20000U, frame->map_start);
+ EXPECT_EQ(0x22000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[2];
+ EXPECT_EQ(2U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x23000U, frame->pc);
+ EXPECT_EQ(0x10070U, frame->sp);
+ EXPECT_EQ("Frame2", frame->function_name);
+ EXPECT_EQ(2U, frame->function_offset);
+ EXPECT_EQ("/fake/libanother.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0x23000U, frame->map_start);
+ EXPECT_EQ(0x24000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify SP in a non-existant map is okay.
+TEST_F(UnwinderTest, sp_not_in_map) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+
+ regs_.FakeSetPc(0x1000);
+ regs_.FakeSetSp(0x53000);
+ ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x50020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ unwinder.Unwind();
+
+ ASSERT_EQ(2U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0x1000U, frame->pc);
+ EXPECT_EQ(0x53000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0x1000U, frame->rel_pc);
+ EXPECT_EQ(0x21000U, frame->pc);
+ EXPECT_EQ(0x50020U, frame->sp);
+ EXPECT_EQ("Frame1", frame->function_name);
+ EXPECT_EQ(1U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libunwind.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0x20000U, frame->map_start);
+ EXPECT_EQ(0x22000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify PC in a device stops the unwind.
+TEST_F(UnwinderTest, pc_in_device_stops_unwind) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+ regs_.FakeSetPc(0x13000);
+ regs_.FakeSetSp(0x10000);
+ ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ unwinder.Unwind();
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+}
+
+// Verify SP in a device stops the unwind.
+TEST_F(UnwinderTest, sp_in_device_stops_unwind) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
+
+ regs_.FakeSetPc(0x1000);
+ regs_.FakeSetSp(0x13000);
+ ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ unwinder.Unwind();
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+}
+
+// Verify a no map info frame gets a frame.
+TEST_F(UnwinderTest, pc_without_map) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+
+ regs_.FakeSetPc(0x41000);
+ regs_.FakeSetSp(0x13000);
+
+ Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ unwinder.Unwind();
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0x41000U, frame->rel_pc);
+ EXPECT_EQ(0x41000U, frame->pc);
+ EXPECT_EQ(0x13000U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_start);
+ EXPECT_EQ(0U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(0, frame->map_flags);
+}
+
+// Verify that a speculative frame is added.
+TEST_F(UnwinderTest, speculative_frame) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+
+ // Fake as if code called a nullptr function.
+ regs_.FakeSetPc(0);
+ regs_.FakeSetSp(0x10000);
+ regs_.FakeSetReturnAddress(0x1202);
+ regs_.FakeSetReturnAddressValid(true);
+
+ ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
+ ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
+
+ Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ unwinder.Unwind();
+
+ ASSERT_EQ(3U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_start);
+ EXPECT_EQ(0U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(0, frame->map_flags);
+
+ frame = &unwinder.frames()[1];
+ EXPECT_EQ(1U, frame->num);
+ EXPECT_EQ(0x200U, frame->rel_pc);
+ EXPECT_EQ(0x1200U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("Frame0", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0x1000U, frame->map_start);
+ EXPECT_EQ(0x8000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+
+ frame = &unwinder.frames()[2];
+ EXPECT_EQ(2U, frame->num);
+ EXPECT_EQ(0x100U, frame->rel_pc);
+ EXPECT_EQ(0x23100U, frame->pc);
+ EXPECT_EQ(0x10020U, frame->sp);
+ EXPECT_EQ("Frame1", frame->function_name);
+ EXPECT_EQ(1U, frame->function_offset);
+ EXPECT_EQ("/fake/libanother.so", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0x23000U, frame->map_start);
+ EXPECT_EQ(0x24000U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(PROT_READ | PROT_WRITE, frame->map_flags);
+}
+
+// Verify that a speculative frame is added then removed because no other
+// frames are added.
+TEST_F(UnwinderTest, speculative_frame_removed) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
+
+ // Fake as if code called a nullptr function.
+ regs_.FakeSetPc(0);
+ regs_.FakeSetSp(0x10000);
+ regs_.FakeSetReturnAddress(0x1202);
+ regs_.FakeSetReturnAddressValid(true);
+
+ Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ unwinder.Unwind();
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ auto* frame = &unwinder.frames()[0];
+ EXPECT_EQ(0U, frame->num);
+ EXPECT_EQ(0U, frame->rel_pc);
+ EXPECT_EQ(0U, frame->pc);
+ EXPECT_EQ(0x10000U, frame->sp);
+ EXPECT_EQ("", frame->function_name);
+ EXPECT_EQ(0U, frame->function_offset);
+ EXPECT_EQ("", frame->map_name);
+ EXPECT_EQ(0U, frame->map_offset);
+ EXPECT_EQ(0U, frame->map_start);
+ EXPECT_EQ(0U, frame->map_end);
+ EXPECT_EQ(0U, frame->map_load_bias);
+ EXPECT_EQ(0, frame->map_flags);
+}
+
+// Verify format frame code.
+TEST_F(UnwinderTest, format_frame_static) {
+ FrameData frame;
+ frame.num = 1;
+ frame.rel_pc = 0x1000;
+ frame.pc = 0x4000;
+ frame.sp = 0x1000;
+ frame.function_name = "function";
+ frame.function_offset = 100;
+ frame.map_name = "/fake/libfake.so";
+ frame.map_offset = 0x2000;
+ frame.map_start = 0x3000;
+ frame.map_end = 0x6000;
+ frame.map_flags = PROT_READ;
+
+ EXPECT_EQ(" #01 pc 0000000000001000 (offset 0x2000) /fake/libfake.so (function+100)",
+ Unwinder::FormatFrame(frame, false));
+ EXPECT_EQ(" #01 pc 00001000 (offset 0x2000) /fake/libfake.so (function+100)",
+ Unwinder::FormatFrame(frame, true));
+
+ frame.map_offset = 0;
+ EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (function+100)",
+ Unwinder::FormatFrame(frame, false));
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100)",
+ Unwinder::FormatFrame(frame, true));
+
+ frame.function_offset = 0;
+ EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (function)",
+ Unwinder::FormatFrame(frame, false));
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function)", Unwinder::FormatFrame(frame, true));
+
+ frame.function_name = "";
+ EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so", Unwinder::FormatFrame(frame, false));
+ EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so", Unwinder::FormatFrame(frame, true));
+
+ frame.map_name = "";
+ EXPECT_EQ(" #01 pc 0000000000001000 <anonymous:3000>", Unwinder::FormatFrame(frame, false));
+ EXPECT_EQ(" #01 pc 00001000 <anonymous:3000>", Unwinder::FormatFrame(frame, true));
+
+ frame.map_start = 0;
+ frame.map_end = 0;
+ EXPECT_EQ(" #01 pc 0000000000001000 <unknown>", Unwinder::FormatFrame(frame, false));
+ EXPECT_EQ(" #01 pc 00001000 <unknown>", Unwinder::FormatFrame(frame, true));
+}
+
+// Verify format frame code.
+TEST_F(UnwinderTest, format_frame) {
+ ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 10));
+
+ regs_.FakeSetPc(0x2300);
+ regs_.FakeSetSp(0x10000);
+
+ Unwinder unwinder(64, &maps_, ®s_, process_memory_);
+ unwinder.Unwind();
+
+ ASSERT_EQ(1U, unwinder.NumFrames());
+
+ regs_.FakeSetMachineType(EM_ARM);
+ EXPECT_EQ(" #00 pc 00001300 /system/fake/libc.so (Frame0+10)", unwinder.FormatFrame(0));
+ regs_.FakeSetMachineType(EM_386);
+ EXPECT_EQ(" #00 pc 00001300 /system/fake/libc.so (Frame0+10)", unwinder.FormatFrame(0));
+
+ regs_.FakeSetMachineType(EM_AARCH64);
+ EXPECT_EQ(" #00 pc 0000000000001300 /system/fake/libc.so (Frame0+10)", unwinder.FormatFrame(0));
+ regs_.FakeSetMachineType(EM_X86_64);
+ EXPECT_EQ(" #00 pc 0000000000001300 /system/fake/libc.so (Frame0+10)", unwinder.FormatFrame(0));
+
+ EXPECT_EQ("", unwinder.FormatFrame(1));
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
index faac2ef..f2530d7 100644
--- a/libunwindstack/tools/unwind.cpp
+++ b/libunwindstack/tools/unwind.cpp
@@ -26,17 +26,11 @@
#include <sys/types.h>
#include <unistd.h>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include <android-base/stringprintf.h>
-
#include <unwindstack/Elf.h>
-#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
+#include <unwindstack/Unwinder.h>
static bool Attach(pid_t pid) {
if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
@@ -55,66 +49,6 @@
return false;
}
-static bool Detach(pid_t pid) {
- return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
-}
-
-std::string GetFrameInfo(size_t frame_num, unwindstack::Regs* regs,
- const std::shared_ptr<unwindstack::Memory>& process_memory,
- unwindstack::MapInfo* map_info, uint64_t* rel_pc) {
- bool bits32;
- switch (regs->MachineType()) {
- case EM_ARM:
- case EM_386:
- bits32 = true;
- break;
-
- default:
- bits32 = false;
- }
-
- if (map_info == nullptr) {
- if (bits32) {
- return android::base::StringPrintf(" #%02zu pc %08" PRIx64, frame_num, regs->pc());
- } else {
- return android::base::StringPrintf(" #%02zu pc %016" PRIx64, frame_num, regs->pc());
- }
- }
-
- unwindstack::Elf* elf = map_info->GetElf(process_memory, true);
- *rel_pc = elf->GetRelPc(regs->pc(), map_info);
- uint64_t adjusted_rel_pc = *rel_pc;
- // Don't need to adjust the first frame pc.
- if (frame_num != 0) {
- adjusted_rel_pc = regs->GetAdjustedPc(*rel_pc, elf);
- }
-
- std::string line;
- if (bits32) {
- line = android::base::StringPrintf(" #%02zu pc %08" PRIx64, frame_num, adjusted_rel_pc);
- } else {
- line = android::base::StringPrintf(" #%02zu pc %016" PRIx64, frame_num, adjusted_rel_pc);
- }
- if (!map_info->name.empty()) {
- line += " " + map_info->name;
- if (map_info->elf_offset != 0) {
- line += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", map_info->elf_offset);
- }
- } else {
- line += android::base::StringPrintf(" <anonymous:%" PRIx64 ">", map_info->offset);
- }
- uint64_t func_offset;
- std::string func_name;
- if (elf->GetFunctionName(adjusted_rel_pc, &func_name, &func_offset)) {
- line += " (" + func_name;
- if (func_offset != 0) {
- line += android::base::StringPrintf("+%" PRId64, func_offset);
- }
- line += ')';
- }
- return line;
-}
-
void DoUnwind(pid_t pid) {
unwindstack::RemoteMaps remote_maps(pid);
if (!remote_maps.Parse()) {
@@ -149,48 +83,12 @@
printf("\n");
auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
- bool return_address_attempt = false;
- std::vector<std::string> frames;
- for (size_t frame_num = 0; frame_num < 64; frame_num++) {
- unwindstack::MapInfo* map_info = remote_maps.Find(regs->pc());
- uint64_t rel_pc;
- frames.push_back(GetFrameInfo(frame_num, regs, process_memory, map_info, &rel_pc));
- bool stepped;
- if (map_info == nullptr) {
- stepped = false;
- } else {
- bool finished;
- stepped =
- map_info->elf->Step(rel_pc + map_info->elf_offset, regs, process_memory.get(), &finished);
- if (stepped && finished) {
- break;
- }
- }
- if (!stepped) {
- if (return_address_attempt) {
- // We tried the return address and it didn't work, remove the last
- // two frames. If this bad frame is the only frame, only remove
- // the last frame.
- frames.pop_back();
- if (frame_num != 1) {
- frames.pop_back();
- }
- break;
- } else {
- // Steping didn't work, try this secondary method.
- if (!regs->SetPcFromReturnAddress(process_memory.get())) {
- break;
- }
- return_address_attempt = true;
- }
- } else {
- return_address_attempt = false;
- }
- }
+ unwindstack::Unwinder unwinder(128, &remote_maps, regs, process_memory);
+ unwinder.Unwind();
// Print the frames.
- for (auto& frame : frames) {
- printf("%s\n", frame.c_str());
+ for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+ printf("%s\n", unwinder.FormatFrame(i).c_str());
}
}
@@ -208,7 +106,7 @@
DoUnwind(pid);
- Detach(pid);
+ ptrace(PTRACE_DETACH, pid, 0, 0);
return 0;
}
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 6e9bddf..1bf5a64 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -124,7 +124,7 @@
},
},
- linux: {
+ linux_glibc: {
srcs: [
"Looper.cpp",
"ProcessCallStack.cpp",
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 24737b9..f5f881f 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -442,6 +442,11 @@
// and all accesses to refs happen before its deletion in the final decWeak.
// The destructor can safely access mRefs because either it's deleting
// mRefs itself, or it's running entirely before the final mWeak decrement.
+ //
+ // Since we're doing atomic loads of `flags`, the static analyzer assumes
+ // they can change between `delete this;` and `refs->decWeak(id);`. This is
+ // not the case. The analyzer may become more okay with this patten when
+ // https://bugs.llvm.org/show_bug.cgi?id=34365 gets resolved. NOLINTNEXTLINE
refs->decWeak(id);
}
diff --git a/libutils/tests/Android.bp b/libutils/tests/Android.bp
index 0869175..6911fc5 100644
--- a/libutils/tests/Android.bp
+++ b/libutils/tests/Android.bp
@@ -44,10 +44,9 @@
"libcutils",
"libutils",
"libbase",
- "libdl",
],
},
- linux: {
+ linux_glibc: {
srcs: [
"Looper_test.cpp",
"RefBase_test.cpp",
@@ -59,7 +58,6 @@
"liblog",
"libbase",
],
- host_ldlibs: ["-ldl"],
},
},
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index f395c74..e3faee3 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -69,11 +69,11 @@
shared_libs: [
"liblog",
"libbase",
+ "libz",
],
target: {
android: {
shared_libs: [
- "libz",
"libutils",
],
},
@@ -81,18 +81,8 @@
static_libs: ["libutils"],
},
linux_bionic: {
- static_libs: ["libz"],
enabled: true,
},
- linux: {
- shared_libs: ["libz-host"],
- },
- darwin: {
- shared_libs: ["libz-host"],
- },
- windows: {
- shared_libs: ["libz-host"],
- },
},
}
@@ -105,7 +95,7 @@
"libziparchive_defaults",
"libziparchive_flags",
],
- shared_libs: ["libz-host"],
+ shared_libs: ["libz"],
static_libs: ["libutils"],
}
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 8ee5ea1..9e1541b 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -1099,7 +1099,7 @@
// and dac_read_search on every try to get past the message
// de-duper. We will also rotate the file name in the directory
// as another measure.
- static const char file[] = "/data/backup/cannot_access_directory_%u";
+ static const char file[] = "/data/drm/cannot_access_directory_%u";
static const unsigned avc_requests_per_access = 2;
rate /= avc_requests_per_access;
diff --git a/reboot/reboot.c b/reboot/reboot.c
index f0cf40c..fe763a8 100644
--- a/reboot/reboot.c
+++ b/reboot/reboot.c
@@ -21,13 +21,13 @@
#include <cutils/android_reboot.h>
#include <unistd.h>
-int main(int argc, char *argv[])
-{
+int main(int argc, char* argv[]) {
int ret;
size_t prop_len;
char property_val[PROPERTY_VALUE_MAX];
- const char *cmd = "reboot";
- char *optarg = "";
+ static const char reboot[] = "reboot";
+ const char* cmd = reboot;
+ char* optarg = "";
opterr = 0;
do {
@@ -60,19 +60,23 @@
prop_len = snprintf(property_val, sizeof(property_val), "%s,%s", cmd, optarg);
if (prop_len >= sizeof(property_val)) {
- fprintf(stderr, "reboot command too long: %s\n", optarg);
+ fprintf(stderr, "%s command too long: %s\n", cmd, optarg);
exit(EXIT_FAILURE);
}
ret = property_set(ANDROID_RB_PROPERTY, property_val);
- if(ret < 0) {
- perror("reboot");
+ if (ret < 0) {
+ perror(cmd);
exit(EXIT_FAILURE);
}
// Don't return early. Give the reboot command time to take effect
// to avoid messing up scripts which do "adb shell reboot && adb wait-for-device"
- while(1) { pause(); }
+ if (cmd == reboot) {
+ while (1) {
+ pause();
+ }
+ }
fprintf(stderr, "Done\n");
return 0;
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 7f86a95..8aa3369 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -7,10 +7,14 @@
dir.system = /system/bin/
dir.system = /system/xbin/
dir.vendor = /vendor/bin/
-dir.test = /data/nativetest/
-dir.test = /data/nativetest64/
-dir.test = /data/benchmarktest/
-dir.test = /data/benchmarktest64/
+dir.vendor = /data/nativetest/vendor
+dir.vendor = /data/nativetest64/vendor
+dir.vendor = /data/benchmarktest/vendor
+dir.vendor = /data/benchmarktest64/vendor
+dir.system = /data/nativetest
+dir.system = /data/nativetest64
+dir.system = /data/benchmarktest
+dir.system = /data/benchmarktest64
[system]
additional.namespaces = sphal,vndk,rs
@@ -98,6 +102,7 @@
# This namespace is exclusively for vndk-sp libs.
###############################################################################
namespace.vndk.isolated = true
+namespace.vndk.visible = true
namespace.vndk.search.paths = /vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp
namespace.vndk.permitted.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl
@@ -121,12 +126,3 @@
namespace.default.search.paths = /vendor/${LIB}/hw:/vendor/${LIB}/egl:/vendor/${LIB}:/system/${LIB}/vndk:/vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/system/${LIB}
namespace.default.asan.search.paths = /data/asan/vendor/${LIB}/hw:/vendor/${LIB}/hw:/data/asan/vendor/${LIB}/egl:/vendor/${LIB}/egl:/data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/system/${LIB}/vndk:/system/${LIB}/vndk:/data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/data/asan/system/${LIB}:/system/${LIB}
-
-###############################################################################
-# Namespace config for tests. No VNDK restriction is enforced for these tests.
-###############################################################################
-[test]
-namespace.default.isolated = false
-namespace.default.search.paths = /vendor/${LIB}:/vendor/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/system/${LIB}
-
-namespace.default.asan.search.paths = /data/asan/vendor/${LIB}:/vendor/${LIB}:/data/asan/vendor/${LIB}/vndk-sp:/vendor/${LIB}/vndk-sp:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp:/data/asan/system/${LIB}:/system/${LIB}
diff --git a/rootdir/etc/ld.config.txt.in b/rootdir/etc/ld.config.txt.in
index af37287..394f0e1 100644
--- a/rootdir/etc/ld.config.txt.in
+++ b/rootdir/etc/ld.config.txt.in
@@ -30,10 +30,10 @@
namespace.default.search.paths = /system/${LIB}
# /vendor/app, /vendor/framework were added since libart should be able to dlopen
# the odex files from the directory.
-namespace.default.permitted.paths = /system/${LIB}/drm:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/data:/mnt/expand
+namespace.default.permitted.paths = /system/${LIB}/drm:/system/${LIB}/extractors:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/data:/mnt/expand
namespace.default.asan.search.paths = /data/asan/system/${LIB}:/system/${LIB}
-namespace.default.asan.permitted.paths = /data:/system/${LIB}/drm:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/mnt/expand
+namespace.default.asan.permitted.paths = /data:/system/${LIB}/drm:/system/${LIB}/extractors:/system/${LIB}/hw:/system/framework:/system/app:/system/priv-app:/vendor/app:/vendor/framework:/oem/app:/mnt/expand
###############################################################################
# "sphal" namespace
@@ -132,9 +132,7 @@
namespace.default.asan.permitted.paths = /data/asan/vendor:/vendor:/data/asan/system/${LIB}/vndk:/system/${LIB}/vndk:/data/asan/system/${LIB}/vndk-sp:/system/${LIB}/vndk-sp
namespace.default.links = system
-namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%:libmedia.so:libandroid_runtime.so
-# libmedia.so must be removed after we have fix for lib-dplmedia.so (b/64427765)
-# libandroid_runtime.so must be removed after we have a fix for qseeproxydaemon (b/64820887)
+namespace.default.link.system.shared_libs = %LLNDK_LIBRARIES%
###############################################################################
# "system" namespace
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index e7b8cc2..b27cfad 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -194,6 +194,7 @@
ScopedMinijail j(minijail_new());
minijail_change_uid(j.get(), uid);
minijail_change_gid(j.get(), gid);
+ minijail_keep_supplementary_gids(j.get());
minijail_enter(j.get());
if (selinux_android_setcontext(uid, 0, info.seinfo, pkgname) < 0) {
diff --git a/trusty/keymaster/trusty_keymaster_device_test.cpp b/trusty/keymaster/trusty_keymaster_device_test.cpp
index e8f5c0b..9227964 100644
--- a/trusty/keymaster/trusty_keymaster_device_test.cpp
+++ b/trusty/keymaster/trusty_keymaster_device_test.cpp
@@ -15,9 +15,9 @@
*/
#include <algorithm>
#include <fstream>
+#include <memory>
#include <gtest/gtest.h>
-#include <nativehelper/UniquePtr.h>
#include <openssl/engine.h>
#include <hardware/keymaster0.h>
@@ -181,7 +181,7 @@
keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
size_t message_len = params.modulus_size / 8;
- UniquePtr<uint8_t[]> message(build_message(message_len));
+ std::unique_ptr<uint8_t[]> message(build_message(message_len));
uint8_t* signature;
size_t siglen;
EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -200,7 +200,7 @@
keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
size_t message_len = params.modulus_size / 8 - 1;
- UniquePtr<uint8_t[]> message(build_message(message_len));
+ std::unique_ptr<uint8_t[]> message(build_message(message_len));
uint8_t* signature;
size_t siglen;
EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, device.sign_data(&sig_params, ptr, size, message.get(),
@@ -217,7 +217,7 @@
keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
size_t message_len = params.modulus_size / 8 + 1;
- UniquePtr<uint8_t[]> message(build_message(message_len));
+ std::unique_ptr<uint8_t[]> message(build_message(message_len));
uint8_t* signature;
size_t siglen;
EXPECT_EQ(KM_ERROR_UNKNOWN_ERROR, device.sign_data(&sig_params, ptr, size, message.get(),
@@ -272,7 +272,7 @@
keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
size_t message_len = 1024 * 7;
- UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
// contents of message don't matter.
uint8_t* signature;
size_t siglen;
@@ -294,7 +294,7 @@
keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
size_t message_len = params.modulus_size / 8;
- UniquePtr<uint8_t[]> message(build_message(message_len));
+ std::unique_ptr<uint8_t[]> message(build_message(message_len));
uint8_t* signature;
size_t siglen;
EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -315,7 +315,7 @@
keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
size_t message_len = params.modulus_size / 8;
- UniquePtr<uint8_t[]> message(build_message(message_len));
+ std::unique_ptr<uint8_t[]> message(build_message(message_len));
uint8_t* signature;
size_t siglen;
EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -338,7 +338,7 @@
keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
size_t message_len = params.modulus_size / 8;
- UniquePtr<uint8_t[]> message(build_message(message_len));
+ std::unique_ptr<uint8_t[]> message(build_message(message_len));
uint8_t* signature;
size_t siglen;
EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -360,7 +360,7 @@
keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
size_t message_len = params.modulus_size / 8;
- UniquePtr<uint8_t[]> message(build_message(message_len));
+ std::unique_ptr<uint8_t[]> message(build_message(message_len));
uint8_t* signature;
size_t siglen;
EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -382,7 +382,7 @@
keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
size_t message_len = params.modulus_size / 8;
- UniquePtr<uint8_t[]> message(build_message(message_len + 1));
+ std::unique_ptr<uint8_t[]> message(build_message(message_len + 1));
uint8_t* signature;
size_t siglen;
EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,
@@ -422,7 +422,7 @@
keymaster_ec_sign_params_t sig_params = {DIGEST_NONE};
size_t message_len = 1024 * 7;
- UniquePtr<uint8_t[]> message(new uint8_t[message_len]);
+ std::unique_ptr<uint8_t[]> message(new uint8_t[message_len]);
// contents of message don't matter.
uint8_t* signature;
size_t siglen;
@@ -453,7 +453,7 @@
keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
size_t message_size = 1024 /* key size */ / 8;
- UniquePtr<uint8_t[]> message(new uint8_t[message_size]);
+ std::unique_ptr<uint8_t[]> message(new uint8_t[message_size]);
memset(message.get(), 'a', message_size);
uint8_t* signature;
size_t siglen;
@@ -491,9 +491,9 @@
static void VerifySignature(const uint8_t* key, size_t key_len, const uint8_t* signature,
size_t signature_len, const uint8_t* message, size_t message_len) {
- UniquePtr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &key, key_len));
+ std::unique_ptr<EVP_PKEY, EVP_PKEY_Delete> pkey(d2i_PUBKEY(NULL, &key, key_len));
ASSERT_TRUE(pkey.get() != NULL);
- UniquePtr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
+ std::unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Delete> ctx(EVP_PKEY_CTX_new(pkey.get(), NULL));
ASSERT_TRUE(ctx.get() != NULL);
ASSERT_EQ(1, EVP_PKEY_verify_init(ctx.get()));
if (EVP_PKEY_type(pkey->type) == EVP_PKEY_RSA)
@@ -518,7 +518,7 @@
// Sign a message so we can verify it with the exported pubkey.
keymaster_rsa_sign_params_t sig_params = {DIGEST_NONE, PADDING_NONE};
size_t message_len = params.modulus_size / 8;
- UniquePtr<uint8_t[]> message(build_message(message_len));
+ std::unique_ptr<uint8_t[]> message(build_message(message_len));
uint8_t* signature;
size_t siglen;
EXPECT_EQ(KM_ERROR_OK, device.sign_data(&sig_params, ptr, size, message.get(), message_len,