Merge "<stdbool.h> not necessary in C++."
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 5b5eff4..304ce4e 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -60,3 +60,4 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/hw/gatekeeper.$(TARGET_DEVICE).so)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/vendor)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/root)
diff --git a/adb/Android.mk b/adb/Android.mk
index d033d5a..05b0284 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -345,18 +345,16 @@
-D_GNU_SOURCE \
-Wno-deprecated-declarations \
-LOCAL_CFLAGS += -DALLOW_ADBD_ROOT=$(if $(filter userdebug eng,$(TARGET_BUILD_VARIANT)),1,0)
LOCAL_CFLAGS += -DALLOW_ADBD_NO_AUTH=$(if $(filter userdebug eng,$(TARGET_BUILD_VARIANT)),1,0)
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
+LOCAL_CFLAGS += -DALLOW_ADBD_ROOT=1
endif
LOCAL_MODULE := adbd
LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
-LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
LOCAL_SANITIZE := $(adb_target_sanitize)
LOCAL_STRIP_MODULE := keep_symbols
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 e7f44c6..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) {
@@ -412,8 +419,13 @@
if (it != usb_handles.end()) {
if (!it->second->device_handle) {
// If the handle is null, we were never able to open the device.
- unregister_usb_transport(it->second.get());
+
+ // Temporarily release the usb handles mutex to avoid deadlock.
+ std::unique_ptr<usb_handle> handle = std::move(it->second);
usb_handles.erase(it);
+ lock.unlock();
+ unregister_usb_transport(handle.get());
+ lock.lock();
} else {
// Closure of the transport will erase the usb_handle.
}
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index e0629ab..1c94298 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -49,23 +49,17 @@
static const char* root_seclabel = nullptr;
-static inline bool is_device_unlocked() {
- return "orange" == android::base::GetProperty("ro.boot.verifiedbootstate", "");
-}
-
static void drop_capabilities_bounding_set_if_needed(struct minijail *j) {
- if (ALLOW_ADBD_ROOT || is_device_unlocked()) {
- if (__android_log_is_debuggable()) {
- return;
- }
+#if defined(ALLOW_ADBD_ROOT)
+ if (__android_log_is_debuggable()) {
+ return;
}
+#endif
minijail_capbset_drop(j, CAP_TO_MASK(CAP_SETUID) | CAP_TO_MASK(CAP_SETGID));
}
static bool should_drop_privileges() {
- // "adb root" not allowed, always drop privileges.
- if (!ALLOW_ADBD_ROOT && !is_device_unlocked()) return true;
-
+#if defined(ALLOW_ADBD_ROOT)
// The properties that affect `adb root` and `adb unroot` are ro.secure and
// ro.debuggable. In this context the names don't make the expected behavior
// particularly obvious.
@@ -95,6 +89,9 @@
}
return drop;
+#else
+ return true; // "adb root" not allowed, always drop privileges.
+#endif // ALLOW_ADBD_ROOT
}
static void drop_privileges(int server_port) {
@@ -161,10 +158,7 @@
// descriptor will always be open.
adbd_cloexec_auth_socket();
- // Respect ro.adb.secure in userdebug/eng builds (ALLOW_ADBD_NO_AUTH), or when the
- // device is unlocked.
- if ((ALLOW_ADBD_NO_AUTH || is_device_unlocked()) &&
- !android::base::GetBoolProperty("ro.adb.secure", false)) {
+ if (ALLOW_ADBD_NO_AUTH && !android::base::GetBoolProperty("ro.adb.secure", false)) {
auth_required = false;
}
diff --git a/adb/services.cpp b/adb/services.cpp
index dbf71d3..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);
}
@@ -150,6 +151,7 @@
sync();
+ if (!reboot_arg || !reboot_arg[0]) reboot_arg = "adb";
std::string reboot_string = android::base::StringPrintf("reboot,%s", reboot_arg);
if (!android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_string)) {
WriteFdFmt(fd, "reboot (%s) failed\n", reboot_string.c_str());
@@ -159,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"
@@ -235,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");
@@ -257,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];
@@ -280,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)) {
@@ -288,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(),
@@ -311,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) {
@@ -483,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/set_verity_enable_state_service.cpp b/adb/set_verity_enable_state_service.cpp
index 253d14a..49e0363 100644
--- a/adb/set_verity_enable_state_service.cpp
+++ b/adb/set_verity_enable_state_service.cpp
@@ -93,21 +93,9 @@
/* Helper function to get A/B suffix, if any. If the device isn't
* using A/B the empty string is returned. Otherwise either "_a",
* "_b", ... is returned.
- *
- * Note that since sometime in O androidboot.slot_suffix is deprecated
- * and androidboot.slot should be used instead. Since bootloaders may
- * be out of sync with the OS, we check both and for extra safety
- * prepend a leading underscore if there isn't one already.
*/
static std::string get_ab_suffix() {
- std::string ab_suffix = android::base::GetProperty("ro.boot.slot_suffix", "");
- if (ab_suffix == "") {
- ab_suffix = android::base::GetProperty("ro.boot.slot", "");
- }
- if (ab_suffix.size() > 0 && ab_suffix[0] != '_') {
- ab_suffix = std::string("_") + ab_suffix;
- }
- return ab_suffix;
+ return android::base::GetProperty("ro.boot.slot_suffix", "");
}
/* Use AVB to turn verity on/off */
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/socket.h b/adb/socket.h
index 4acdf4a..64d05a9 100644
--- a/adb/socket.h
+++ b/adb/socket.h
@@ -19,84 +19,83 @@
#include <stddef.h>
+#include <memory>
+
#include "fdevent.h"
struct apacket;
class atransport;
/* An asocket represents one half of a connection between a local and
-** remote entity. A local asocket is bound to a file descriptor. A
-** remote asocket is bound to the protocol engine.
-*/
+ * remote entity. A local asocket is bound to a file descriptor. A
+ * remote asocket is bound to the protocol engine.
+ */
struct asocket {
- /* chain pointers for the local/remote list of
- ** asockets that this asocket lives in
- */
- asocket *next;
- asocket *prev;
+ /* chain pointers for the local/remote list of
+ * asockets that this asocket lives in
+ */
+ asocket* next;
+ asocket* prev;
- /* the unique identifier for this asocket
- */
+ /* the unique identifier for this asocket
+ */
unsigned id;
- /* flag: set when the socket's peer has closed
- ** but packets are still queued for delivery
- */
- int closing;
+ /* flag: set when the socket's peer has closed
+ * but packets are still queued for delivery
+ */
+ int closing;
// flag: set when the socket failed to write, so the socket will not wait to
// write packets and close directly.
bool has_write_error;
- /* flag: quit adbd when both ends close the
- ** local service socket
- */
- int exit_on_close;
+ /* flag: quit adbd when both ends close the
+ * local service socket
+ */
+ int exit_on_close;
- /* the asocket we are connected to
- */
+ // the asocket we are connected to
+ asocket* peer;
- asocket *peer;
-
- /* For local asockets, the fde is used to bind
- ** us to our fd event system. For remote asockets
- ** these fields are not used.
- */
+ /* For local asockets, the fde is used to bind
+ * us to our fd event system. For remote asockets
+ * these fields are not used.
+ */
fdevent fde;
int fd;
- /* queue of apackets waiting to be written
- */
- apacket *pkt_first;
- apacket *pkt_last;
+ // queue of apackets waiting to be written
+ apacket* pkt_first;
+ apacket* pkt_last;
- /* enqueue is called by our peer when it has data
- ** for us. It should return 0 if we can accept more
- ** data or 1 if not. If we return 1, we must call
- ** peer->ready() when we once again are ready to
- ** receive data.
- */
- int (*enqueue)(asocket *s, apacket *pkt);
+ /* enqueue is called by our peer when it has data
+ * for us. It should return 0 if we can accept more
+ * data or 1 if not. If we return 1, we must call
+ * peer->ready() when we once again are ready to
+ * receive data.
+ */
+ int (*enqueue)(asocket* s, apacket* pkt);
- /* ready is called by the peer when it is ready for
- ** us to send data via enqueue again
- */
- void (*ready)(asocket *s);
+ /* ready is called by the peer when it is ready for
+ * us to send data via enqueue again
+ */
+ void (*ready)(asocket* s);
- /* shutdown is called by the peer before it goes away.
- ** the socket should not do any further calls on its peer.
- ** Always followed by a call to close. Optional, i.e. can be NULL.
- */
- void (*shutdown)(asocket *s);
+ /* shutdown is called by the peer before it goes away.
+ * the socket should not do any further calls on its peer.
+ * Always followed by a call to close. Optional, i.e. can be NULL.
+ */
+ void (*shutdown)(asocket* s);
- /* close is called by the peer when it has gone away.
- ** we are not allowed to make any further calls on the
- ** peer once our close method is called.
- */
- void (*close)(asocket *s);
+ /* close is called by the peer when it has gone away.
+ * we are not allowed to make any further calls on the
+ * peer once our close method is called.
+ */
+ void (*close)(asocket* s);
- /* A socket is bound to atransport */
- atransport *transport;
+ /* A socket is bound to atransport */
+ atransport* transport;
size_t get_max_payload() const;
};
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index f28a3df..c53fbb4 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -96,7 +96,7 @@
}
void remove_socket(asocket* s) {
- // socket_list_lock should already be held
+ std::lock_guard<std::recursive_mutex> lock(local_socket_list_lock);
if (s->prev && s->next) {
s->prev->next = s->next;
s->next->prev = s->prev;
diff --git a/adb/test_device.py b/adb/test_device.py
index 9e1a2ec..ddceda9 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -1237,7 +1237,7 @@
return m.group(2)
return None
- def test_killed_when_pushing_a_large_file(self):
+ def disabled_test_killed_when_pushing_a_large_file(self):
"""
While running adb push with a large file, kill adb server.
Occasionally the device becomes offline. Because the device is still
@@ -1268,7 +1268,7 @@
# 4. The device should be online
self.assertEqual(self._get_device_state(serialno), 'device')
- def test_killed_when_pulling_a_large_file(self):
+ def disabled_test_killed_when_pulling_a_large_file(self):
"""
While running adb pull with a large file, kill adb server.
Occasionally the device can't be connected. Because the device is trying to
diff --git a/adb/transport.cpp b/adb/transport.cpp
index b2e03a0..089a1ec 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -615,15 +615,15 @@
static void transport_unref(atransport* t) {
CHECK(t != nullptr);
- size_t old_refcount = t->ref_count--;
- CHECK_GT(old_refcount, 0u);
-
- if (old_refcount == 1u) {
+ std::lock_guard<std::recursive_mutex> lock(transport_lock);
+ CHECK_GT(t->ref_count, 0u);
+ t->ref_count--;
+ if (t->ref_count == 0) {
D("transport: %s unref (kicking and closing)", t->serial);
t->close(t);
remove_transport(t);
} else {
- D("transport: %s unref (count=%zu)", t->serial, old_refcount - 1);
+ D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
}
}
@@ -748,9 +748,6 @@
}
int atransport::Write(apacket* p) {
-#if ADB_HOST
- std::lock_guard<std::mutex> lock(write_msg_lock_);
-#endif
return write_func_(p, this);
}
@@ -758,11 +755,6 @@
if (!kicked_) {
kicked_ = true;
CHECK(kick_func_ != nullptr);
-#if ADB_HOST
- // On host, adb server should avoid writing part of a packet, so don't
- // kick a transport whiling writing a packet.
- std::lock_guard<std::mutex> lock(write_msg_lock_);
-#endif
kick_func_(this);
}
}
@@ -1109,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 00fad56..8c101fd 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -64,7 +64,7 @@
// it's better to do this piece by piece.
atransport(ConnectionState state = kCsOffline)
- : id(NextTransportId()), ref_count(0), connection_state_(state) {
+ : id(NextTransportId()), connection_state_(state) {
transport_fde = {};
protocol_version = A_VERSION;
max_payload = MAX_PAYLOAD;
@@ -88,7 +88,7 @@
int fd = -1;
int transport_socket = -1;
fdevent transport_fde;
- std::atomic<size_t> ref_count;
+ size_t ref_count = 0;
uint32_t sync_token = 0;
bool online = false;
TransportType type = kTransportAny;
@@ -122,7 +122,6 @@
#if ADB_HOST
std::shared_ptr<RSA> NextKey();
- bool SetSendConnectOnError();
#endif
char token[TOKEN_SIZE] = {};
@@ -181,8 +180,6 @@
std::atomic<ConnectionState> connection_state_;
#if ADB_HOST
std::deque<std::shared_ptr<RSA>> keys_;
- std::mutex write_msg_lock_;
- 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/base/include/android-base/logging.h b/base/include/android-base/logging.h
index 548b286..f93c696 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -438,4 +438,36 @@
} // namespace base
} // namespace android
+namespace std {
+
+// Emit a warning of ostream<< with std::string*. The intention was most likely to print *string.
+//
+// Note: for this to work, we need to have this in a namespace.
+// Note: lots of ifdef magic to make this work with Clang (platform) vs GCC (windows tools)
+// Note: using diagnose_if(true) under Clang and nothing under GCC/mingw as there is no common
+// attribute support.
+// Note: using a pragma because "-Wgcc-compat" (included in "-Weverything") complains about
+// diagnose_if.
+// Note: to print the pointer, use "<< static_cast<const void*>(string_pointer)" instead.
+// Note: a not-recommended alternative is to let Clang ignore the warning by adding
+// -Wno-user-defined-warnings to CPPFLAGS.
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wgcc-compat"
+#define OSTREAM_STRING_POINTER_USAGE_WARNING \
+ __attribute__((diagnose_if(true, "Unexpected logging of string pointer", "warning")))
+#else
+#define OSTREAM_STRING_POINTER_USAGE_WARNING /* empty */
+#endif
+inline std::ostream& operator<<(std::ostream& stream, const std::string* string_pointer)
+ OSTREAM_STRING_POINTER_USAGE_WARNING {
+ return stream << static_cast<const void*>(string_pointer);
+}
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+#undef OSTREAM_STRING_POINTER_USAGE_WARNING
+
+} // namespace std
+
#endif // ANDROID_BASE_LOGGING_H
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index c0bf0c1..07a5edd 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -26,6 +26,10 @@
TemporaryFile();
~TemporaryFile();
+ // Release the ownership of fd, caller is reponsible for closing the
+ // fd or stream properly.
+ int release();
+
int fd;
char path[1024];
diff --git a/base/properties.cpp b/base/properties.cpp
index 816bca0..cde4d69 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -36,13 +36,18 @@
const prop_info* pi = __system_property_find(key.c_str());
if (pi == nullptr) return default_value;
- char buf[PROP_VALUE_MAX];
- if (__system_property_read(pi, nullptr, buf) > 0) return buf;
+ std::string property_value;
+ __system_property_read_callback(pi,
+ [](void* cookie, const char*, const char* value, unsigned) {
+ auto property_value = reinterpret_cast<std::string*>(cookie);
+ *property_value = value;
+ },
+ &property_value);
// If the property exists but is empty, also return the default value.
// Since we can't remove system properties, "empty" is traditionally
// the same as "missing" (this was true for cutils' property_get).
- return default_value;
+ return property_value.empty() ? default_value : property_value;
}
bool GetBoolProperty(const std::string& key, bool default_value) {
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 636477d..1cfa9e6 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -85,10 +85,18 @@
}
TemporaryFile::~TemporaryFile() {
- close(fd);
+ if (fd != -1) {
+ close(fd);
+ }
unlink(path);
}
+int TemporaryFile::release() {
+ int result = fd;
+ fd = -1;
+ return result;
+}
+
void TemporaryFile::init(const std::string& tmp_dir) {
snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(),
OS_PATH_SEPARATOR);
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index dd357ed..2c87018 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -63,7 +63,13 @@
name: "bootstat",
defaults: ["bootstat_defaults"],
static_libs: ["libbootstat"],
+ shared_libs: ["liblogcat"],
init_rc: ["bootstat.rc"],
+ product_variables: {
+ debuggable: {
+ init_rc: ["bootstat-debug.rc"],
+ },
+ },
srcs: ["bootstat.cpp"],
}
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index 99d9405..e2a4b04 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -58,16 +58,15 @@
}
void BootEventRecordStore::AddBootEvent(const std::string& event) {
- auto uptime = std::chrono::duration_cast<std::chrono::seconds>(
- android::base::boot_clock::now().time_since_epoch());
- AddBootEventWithValue(event, uptime.count());
+ auto uptime = std::chrono::duration_cast<std::chrono::seconds>(
+ android::base::boot_clock::now().time_since_epoch());
+ AddBootEventWithValue(event, uptime.count());
}
// The implementation of AddBootEventValue makes use of the mtime file
// attribute to store the value associated with a boot event in order to
// optimize on-disk size requirements and small-file thrashing.
-void BootEventRecordStore::AddBootEventWithValue(
- const std::string& event, int32_t value) {
+void BootEventRecordStore::AddBootEventWithValue(const std::string& event, int32_t value) {
std::string record_path = GetBootEventPath(event);
int record_fd = creat(record_path.c_str(), S_IRUSR | S_IWUSR);
if (record_fd == -1) {
@@ -96,8 +95,7 @@
close(record_fd);
}
-bool BootEventRecordStore::GetBootEvent(
- const std::string& event, BootEventRecord* record) const {
+bool BootEventRecordStore::GetBootEvent(const std::string& event, BootEventRecord* record) const {
CHECK_NE(static_cast<BootEventRecord*>(nullptr), record);
CHECK(!event.empty());
@@ -112,8 +110,7 @@
return true;
}
-std::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore::
- GetAllBootEvents() const {
+std::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore::GetAllBootEvents() const {
std::vector<BootEventRecord> events;
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(store_path_.c_str()), closedir);
@@ -147,8 +144,7 @@
store_path_ = path;
}
-std::string BootEventRecordStore::GetBootEventPath(
- const std::string& event) const {
+std::string BootEventRecordStore::GetBootEventPath(const std::string& event) const {
DCHECK_EQ('/', store_path_.back());
return store_path_ + event;
}
diff --git a/bootstat/boot_event_record_store.h b/bootstat/boot_event_record_store.h
index a2b8318..f872c85 100644
--- a/bootstat/boot_event_record_store.h
+++ b/bootstat/boot_event_record_store.h
@@ -17,12 +17,12 @@
#ifndef BOOT_EVENT_RECORD_STORE_H_
#define BOOT_EVENT_RECORD_STORE_H_
+#include <android-base/macros.h>
+#include <gtest/gtest_prod.h>
#include <cstdint>
#include <string>
#include <utility>
#include <vector>
-#include <android-base/macros.h>
-#include <gtest/gtest_prod.h>
// BootEventRecordStore manages the persistence of boot events to the record
// store and the retrieval of all boot event records from the store.
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index d98169b..4b7ab36 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -94,20 +94,16 @@
// Returns the time in seconds since boot.
time_t GetUptimeSeconds() {
- return std::chrono::duration_cast<std::chrono::seconds>(
- android::base::boot_clock::now().time_since_epoch())
- .count();
+ return std::chrono::duration_cast<std::chrono::seconds>(
+ android::base::boot_clock::now().time_since_epoch())
+ .count();
}
class BootEventRecordStoreTest : public ::testing::Test {
public:
- BootEventRecordStoreTest() {
- store_path_ = std::string(store_dir_.path) + "/";
- }
+ BootEventRecordStoreTest() { store_path_ = std::string(store_dir_.path) + "/"; }
- const std::string& GetStorePathForTesting() const {
- return store_path_;
- }
+ const std::string& GetStorePathForTesting() const { return store_path_; }
private:
void TearDown() {
@@ -159,9 +155,7 @@
store.AddBootEvent("triassic");
const std::string EXPECTED_NAMES[] = {
- "cretaceous",
- "jurassic",
- "triassic",
+ "cretaceous", "jurassic", "triassic",
};
auto events = store.GetAllBootEvents();
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
new file mode 100755
index 0000000..209e81b
--- /dev/null
+++ b/bootstat/boot_reason_test.sh
@@ -0,0 +1,1069 @@
+#! /bin/bash
+#
+# Bootstat boot reason tests
+#
+# throughout testing:
+# - manual tests can only run on eng/userdebug builds
+# - watch adb logcat -b all -d -s bootstat
+# - watch adb logcat -b all -d | audit2allow
+# - wait until screen is up, boot has completed, can mean wait for
+# sys.boot_completed=1 and sys.logbootcomplete=1 to be true
+#
+# All test frames, and nothing else, must be function names prefixed and
+# specifiged with the pattern 'test_<test>() {' as this is also how the
+# script discovers the full list of tests by inspecting its own code.
+#
+
+# Helper variables
+
+SPACE=" "
+ESCAPE=""
+TAB=" "
+GREEN="${ESCAPE}[38;5;40m"
+RED="${ESCAPE}[38;5;196m"
+NORMAL="${ESCAPE}[0m"
+# Best guess to an average device's reboot time, refined as tests return
+DURATION_DEFAULT=45
+
+# Helper functions
+
+[ "USAGE: inFastboot
+
+Returns: true if device is in fastboot mode" ]
+inFastboot() {
+ fastboot devices | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+}
+
+[ "USAGE: inAdb
+
+Returns: true if device is in adb mode" ]
+inAdb() {
+ adb devices | grep -v 'List of devices attached' | grep "^${ANDROID_SERIAL}[${SPACE}${TAB}]" > /dev/null
+}
+
+[ "USAGE: hasPstore
+
+Returns: true if device (likely) has pstore data" ]
+hasPstore() {
+ if inAdb && [ 0 -eq `adb shell su root ls /sys/fs/pstore | wc -l` ]; then
+ false
+ fi
+}
+
+[ "USAGE: isDebuggable
+
+Returns: true if device is (likely) a debug build" ]
+isDebuggable() {
+ if inAdb && [ 1 -ne `adb shell getprop ro.debuggable` ]; then
+ false
+ fi
+}
+
+[ "USAGE: checkDebugBuild [--noerror]
+
+Returns: true if device is a userdebug or eng release" ]
+checkDebugBuild() {
+ if isDebuggable; then
+ echo "INFO: '${TEST}' test requires userdebug build"
+ elif [ -n "${1}" ]; then
+ echo "WARNING: '${TEST}' test requires userdebug build"
+ false
+ else
+ echo "ERROR: '${TEST}' test requires userdebug build, skipping FAILURE"
+ duration_prefix="~"
+ duration_estimate=1
+ false
+ fi >&2
+}
+
+[ "USAGE: setBootloaderBootReason [value]
+
+Returns: true if device supports and set boot reason injection" ]
+setBootloaderBootReason() {
+ inAdb || ( echo "ERROR: device not in adb mode." >&2 ; false ) || return 1
+ if [ -z "`adb shell ls /etc/init/bootstat-debug.rc 2>/dev/null`" ]; then
+ echo "ERROR: '${TEST}' test requires /etc/init/bootstat-debug.rc" >&2
+ return 1
+ fi
+ checkDebugBuild || return 1
+ if adb shell su root "cat /proc/cmdline | tr '\\0 ' '\\n\\n'" |
+ grep '^androidboot[.]bootreason=[^ ]' >/dev/null; then
+ echo "ERROR: '${TEST}' test requires a device with a bootloader that" >&2
+ echo " does not set androidboot.bootreason kernel parameter." >&2
+ return 1
+ fi
+ adb shell su root setprop persist.test.boot.reason "'${1}'" 2>/dev/null
+ test_reason="`adb shell getprop persist.test.boot.reason 2>/dev/null`"
+ if [ X"${test_reason}" != X"${1}" ]; then
+ echo "ERROR: can not set persist.test.boot.reason to '${1}'." >&2
+ return 1
+ fi
+}
+
+[ "USAGE: enterPstore
+
+Prints a warning string requiring functional pstore
+
+Returns: pstore_ok variable set to true or false" ]
+enterPstore() {
+ if hasPstore; then
+ echo "INFO: '${TEST}' test requires functional and reliable pstore"
+ pstore_ok=true
+ else
+ echo "WARNING: '${TEST}' test requires functional pstore"
+ pstore_ok=false
+ fi >&2
+ ${pstore_ok}
+}
+
+[ "USAGE: exitPstore
+
+Prints an error string requiring functional pstore
+
+Returns: clears error if pstore dysfunctional" ]
+exitPstore() {
+ save_ret=${?}
+ if [ ${save_ret} != 0 ]; then
+ if hasPstore; then
+ return ${save_ret}
+ fi
+ if [ true = ${pstore_ok} ]; then
+ echo "WARNING: '${TEST}' test requires functional pstore"
+ return ${save_ret}
+ fi
+ echo "ERROR: '${TEST}' test requires functional pstore, skipping FAILURE"
+ duration_prefix="~"
+ duration_estimate=1
+ fi >&2
+}
+
+[ "USAGE: format_duration <seconds>
+
+human readable output whole seconds, whole minutes or mm:ss" ]
+format_duration() {
+ if [ -z "${1}" ]; then
+ echo unknown
+ return
+ fi
+ seconds=`expr ${1} % 60`
+ minutes=`expr ${1} / 60`
+ if [ 0 -eq ${minutes} ]; then
+ if [ 1 -eq ${1} ]; then
+ echo 1 second
+ return
+ fi
+ echo ${1} seconds
+ return
+ elif [ 60 -eq ${1} ]; then
+ echo 1 minute
+ return
+ elif [ 0 -eq ${seconds} ]; then
+ echo ${minutes} minutes
+ return
+ fi
+ echo ${minutes}:`expr ${seconds} / 10``expr ${seconds} % 10`
+}
+
+wait_for_screen_timeout=900
+[ "USAGE: wait_for_screen [-n] [TIMEOUT]
+
+-n - echo newline at exit
+TIMEOUT - default `format_duration ${wait_for_screen_timeout}`" ]
+wait_for_screen() {
+ exit_function=true
+ if [ X"-n" = X"${1}" ]; then
+ exit_function=echo
+ shift
+ fi
+ timeout=${wait_for_screen_timeout}
+ if [ ${#} -gt 0 ]; then
+ timeout=${1}
+ shift
+ fi
+ counter=0
+ while true; do
+ if inFastboot; then
+ fastboot reboot
+ elif inAdb; then
+ if [ 0 != ${counter} ]; then
+ adb wait-for-device </dev/null >/dev/null 2>/dev/null
+ fi
+ if [ -n "`adb shell getprop sys.boot.reason </dev/null 2>/dev/null`" ]
+ then
+ vals=`adb shell getprop </dev/null 2>/dev/null |
+ sed -n 's/[[]sys[.]\(boot_completed\|logbootcomplete\)[]]: [[]\([01]\)[]]$/\1=\2/p'`
+ if [ "${vals}" = "`echo boot_completed=1 ; echo logbootcomplete=1`" ]
+ then
+ break
+ fi
+ if [ "${vals}" = "`echo logbootcomplete=1 ; echo boot_completed=1`" ]
+ then
+ break
+ fi
+ fi
+ fi
+ counter=`expr ${counter} + 1`
+ if [ ${counter} -gt ${timeout} ]; then
+ ${exit_function}
+ echo "ERROR: wait_for_screen() timed out (`format_duration ${timeout}`)" >&2
+ return 1
+ fi
+ sleep 1
+ done
+ ${exit_function}
+}
+
+[ "USAGE: EXPECT_EQ <lval> <rval> [message]
+
+Returns true if (regex) lval matches rval" ]
+EXPECT_EQ() {
+ lval="${1}"
+ rval="${2}"
+ shift 2
+ if ! ( echo X"${rval}" | grep '^X'"${lval}"'$' >/dev/null 2>/dev/null ); then
+ echo "ERROR: expected \"${lval}\" got \"${rval}\"" >&2
+ if [ -n "${*}" ] ; then
+ echo " ${*}" >&2
+ fi
+ return 1
+ fi
+ if [ -n "${*}" ] ; then
+ if [ X"${lval}" != X"${rval}" ]; then
+ echo "INFO: ok \"${lval}\"(=\"${rval}\") ${*}" >&2
+ else
+ echo "INFO: ok \"${lval}\" ${*}" >&2
+ fi
+ fi
+ return 0
+}
+
+[ "USAGE: EXPECT_PROPERTY <prop> <value> [--allow_failure]
+
+Returns true if current return (regex) value is true and the result matches" ]
+EXPECT_PROPERTY() {
+ save_ret=${?}
+ property="${1}"
+ value="${2}"
+ shift 2
+ val=`adb shell getprop ${property} 2>&1`
+ EXPECT_EQ "${value}" "${val}" for Android property ${property} ||
+ [ -n "${1}" ] ||
+ save_ret=${?}
+ return ${save_ret}
+}
+
+[ "USAGE: report_bootstat_logs <expected> ...
+
+if not prefixed with a minus (-), <expected> will become a series of expected
+matches:
+
+ bootstat: Canonical boot reason: <expected_property_value>
+
+If prefixed with a minus, <expected> will look for an exact match after
+removing the minux prefix. All expected content is _dropped_ from the output
+and in essence forms a known blacklist, unexpected content will show.
+
+Report any logs, minus a known blacklist, preserve the current exit status" ]
+report_bootstat_logs() {
+ save_ret=${?}
+ match=
+ for i in "${@}"; do
+ if [ X"${i}" != X"${i#-}" ] ; then
+ match="${match}
+${i#-}"
+ else
+ match="${match}
+bootstat: Canonical boot reason: ${i}"
+ fi
+ done
+ adb logcat -b all -d |
+ grep bootstat[^e] |
+ grep -v -F "bootstat: Service started: /system/bin/bootstat --record_boot_complete${match}
+bootstat: Failed to read /data/misc/bootstat/post_decrypt_time_elapsed: No such file or directory
+bootstat: Failed to parse boot time record: /data/misc/bootstat/post_decrypt_time_elapsed
+bootstat: Service started: /system/bin/bootstat --record_boot_reason
+bootstat: Service started: /system/bin/bootstat --record_time_since_factory_reset
+bootstat: Service started: /system/bin/bootstat -l
+bootstat: Battery level at shutdown 100%
+bootstat: Battery level at startup 100%
+init : Parsing file /system/etc/init/bootstat.rc...
+init : processing action (persist.test.boot.reason=*) from (/system/etc/init/bootstat-debug.rc:
+init : Command 'setprop ro.boot.bootreason \${persist.test.boot.reason}' action=persist.test.boot.reason=* (/system/etc/init/bootstat-debug.rc:
+init : processing action (post-fs-data) from (/system/etc/init/bootstat.rc
+init : processing action (boot) from (/system/etc/init/bootstat.rc
+init : processing action (ro.boot.bootreason=*) from (/system/etc/init/bootstat.rc
+init : processing action (sys.boot_completed=1 && sys.logbootcomplete=1) from (/system/etc/init/bootstat.rc
+ (/system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'
+ (/system/bin/bootstat -r post_decrypt_time_elapsed)'
+init : Command 'exec - system log -- /system/bin/bootstat --record_boot_complete' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
+init : Command 'exec - system log -- /system/bin/bootstat --record_boot_reason' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
+init : Command 'exec - system log -- /system/bin/bootstat --record_time_since_factory_reset' action=sys.boot_completed=1 && sys.logbootcomplete=1 (/system/etc/init/bootstat.rc:
+ (/system/bin/bootstat --record_boot_complete)'...
+ (/system/bin/bootstat --record_boot_complete)' (pid${SPACE}
+ (/system/bin/bootstat --record_boot_reason)'...
+ (/system/bin/bootstat --record_boot_reason)' (pid${SPACE}
+ (/system/bin/bootstat --record_time_since_factory_reset)'...
+ (/system/bin/bootstat --record_time_since_factory_reset)' (pid${SPACE}
+ (/system/bin/bootstat -l)'...
+ (/system/bin/bootstat -l)' (pid " |
+ grep -v 'bootstat: Unknown boot reason: $' # Hikey Special
+ return ${save_ret}
+}
+
+[ "USAGE: start_test [message]
+
+Record start of test, preserve exit status" ]
+start_test() {
+ save_ret=${?}
+ duration_prefix="~"
+ duration_estimate=1
+ START=`date +%s`
+ echo "${GREEN}[ RUN ]${NORMAL} ${TEST} ${*}"
+ return ${save_ret}
+}
+
+duration_sum_diff=0
+duration_num=0
+[ "USAGE: duration_test [[prefix]seconds]
+
+Report the adjusted and expected test duration" ]
+duration_test() {
+ duration_prefix=${1%%[0123456789]*}
+ if [ -z "${duration_prefix}" ]; then
+ duration_prefix="~"
+ fi
+ duration_estimate="${1#${duration_prefix}}"
+ if [ -z "${duration_estimate}" ]; then
+ duration_estimate="${DURATION_DEFAULT}"
+ fi
+ duration_new_estimate="${duration_estimate}"
+ if [ 0 -ne ${duration_num} ]; then
+ duration_new_estimate=`expr ${duration_new_estimate} + \
+ \( ${duration_num} / 2 + ${duration_sum_diff} \) / ${duration_num}`
+ # guard against catastrophe
+ if [ -z "${duration_new_estimate}" ]; then
+ duration_new_estimate=${duration_estimate}
+ fi
+ fi
+ # negative values are so undignified
+ if [ 0 -ge ${duration_new_estimate} ]; then
+ duration_new_estimate=1
+ fi
+ echo "INFO: expected duration of '${TEST}' test" \
+ "${duration_prefix}`format_duration ${duration_new_estimate}`" >&2
+}
+
+[ "USAGE: end_test [message]
+
+Document duration and success of test, preserve exit status" ]
+end_test() {
+ save_ret=${?}
+ END=`date +%s`
+ duration=`expr ${END} - ${START} 2>/dev/null`
+ [ 0 -ge ${duration} ] ||
+ echo "INFO: '${TEST}' test duration `format_duration ${duration}`" >&2
+ if [ ${save_ret} = 0 ]; then
+ if [ 0 -lt ${duration} -a 0 -lt ${duration_estimate} -a \( \
+ X"~" = X"${duration_prefix}" -o \
+ ${duration_estimate} -gt ${duration} \) ]; then
+ duration_sum_diff=`expr ${duration_sum_diff} + \
+ ${duration} - ${duration_estimate}`
+ duration_num=`expr ${duration_num} + 1`
+ fi
+ echo "${GREEN}[ OK ]${NORMAL} ${TEST} ${*}"
+ else
+ echo "${RED}[ FAILED ]${NORMAL} ${TEST} ${*}"
+ fi
+ return ${save_ret}
+}
+
+[ "USAGE: wrap_test <test> [message]
+
+All tests below are wrapped with this helper" ]
+wrap_test() {
+ if [ -z "${1}" -o X"nothing" = X"${1}" ]; then
+ return
+ fi
+ TEST=${1}
+ shift
+ start_test ${1}
+ eval test_${TEST}
+ end_test ${2}
+}
+
+[ "USAGE: validate_reason <value>
+
+Check property for CTS compliance with our expectations. Return a cleansed
+string representing what is acceptable.
+
+NB: must also roughly match heuristics in system/core/bootstat/bootstat.cpp" ]
+validate_reason() {
+ var=`echo -n ${*} |
+ tr '[A-Z]' '[a-z]' |
+ tr ' \f\t\r\n' '_____'`
+ case ${var} in
+ watchdog | watchdog,?* ) ;;
+ kernel_panic | kernel_panic,?*) ;;
+ recovery | recovery,?*) ;;
+ bootloader | bootloader,?*) ;;
+ cold | cold,?*) ;;
+ hard | hard,?*) ;;
+ warm | warm,?*) ;;
+ shutdown | shutdown,?*) ;;
+ reboot,reboot | reboot,reboot,* ) var=${var#reboot,} ; var=${var%,} ;;
+ reboot,cold | reboot,cold,* ) var=${var#reboot,} ; var=${var%,} ;;
+ reboot,hard | reboot,hard,* ) var=${var#reboot,} ; var=${var%,} ;;
+ reboot,warm | reboot,warm,* ) var=${var#reboot,} ; var=${var%,} ;;
+ reboot,recovery | reboot,recovery,* ) var=${var#reboot,} ; var=${var%,} ;;
+ reboot,bootloader | reboot,bootloader,* ) var=${var#reboot,} ; var=${var%,} ;;
+ reboot | reboot,?*) ;;
+ # Aliases and Heuristics
+ *wdog* | *watchdog* ) var="watchdog" ;;
+ *powerkey* ) var="cold,powerkey" ;;
+ *panic* | *kernel_panic*) var="kernel_panic" ;;
+ *thermal*) var="shutdown,thermal" ;;
+ *s3_wakeup*) var="warm,s3_wakeup" ;;
+ *hw_reset*) var="hard,hw_reset" ;;
+ *bootloader*) var="bootloader" ;;
+ *) var="reboot" ;;
+ esac
+ echo ${var}
+}
+
+[ "USAGE: validate_property <property>
+
+Check property for CTS compliance with our expectations. Return a cleansed
+string representing what is acceptable.
+
+NB: must also roughly match heuristics in system/core/bootstat/bootstat.cpp" ]
+validate_property() {
+ val="`adb shell getprop ${1} 2>&1`"
+ ret=`validate_reason "${val}"`
+ if [ "reboot" = "${ret}" ]; then
+ ret=`validate_reason "reboot,${val}"`
+ fi
+ echo ${ret}
+}
+
+#
+# Actual test frames
+#
+
+[ "USAGE: test_properties
+
+properties test
+- (wait until screen is up, boot has completed)
+- adb shell getprop ro.boot.bootreason (bootloader reason)
+- adb shell getprop persist.sys.boot.reason (last reason)
+- adb shell getprop sys.boot.reason (system reason)
+- NB: all should have a value that is compliant with our known set." ]
+test_properties() {
+ duration_test 1
+ wait_for_screen
+ retval=0
+ check_set="ro.boot.bootreason persist.sys.boot.reason sys.boot.reason"
+ bootloader=""
+ # NB: this test could fail if performed _after_ optional_factory_reset test
+ # and will report
+ # ERROR: expected "reboot" got ""
+ # for Android property persist.sys.boot.reason
+ # following is mitigation for the persist.sys.boot.reason, skip it
+ if [ "reboot,factory_reset" = "`validate_property ro.boot_bootreason`" ]; then
+ check_set="ro.boot.bootreason sys.boot.reason"
+ bootloader="bootloader"
+ fi
+ for prop in ${check_set}; do
+ reason=`validate_property ${prop}`
+ EXPECT_PROPERTY ${prop} ${reason} || retval=${?}
+ done
+ # sys.boot.reason is last for a reason
+ report_bootstat_logs ${reason} ${bootloader}
+ return ${retval}
+}
+
+[ "USAGE: test_ota
+
+ota test
+- rm out/.kati_stamp-* out/build_date.txt out/build_number.txt
+- rm out/target/product/*/*/*.prop
+- rm -r out/target/product/*/obj/ETC/system_build_prop_intermediates
+- m
+- NB: ro.build.date.utc should update
+- fastboot flashall
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report ota
+
+Decision to change the build itself rather than trick bootstat by
+rummaging through its data files was made." ]
+test_ota() {
+ duration_test ">300"
+ echo " extended by build and flashing times" >&2
+ if [ -z "${TARGET_PRODUCT}" -o \
+ -z "${ANDROID_PRODUCT_OUT}" -o \
+ -z "${ANDROID_BUILD_TOP}" -o \
+ -z "${TARGET_BUILD_VARIANT}" ]; then
+ echo "ERROR: Missing envsetup.sh and lunch" >&2
+ return 1
+ fi
+ rm ${ANDROID_PRODUCT_OUT%/out/*}/out/.kati_stamp-* ||
+ true
+ rm ${ANDROID_PRODUCT_OUT%/out/*}/out/build_date.txt ||
+ true
+ rm ${ANDROID_PRODUCT_OUT%/out/*}/out/build_number.txt ||
+ true
+ rm ${ANDROID_PRODUCT_OUT}/*/*.prop ||
+ true
+ rm -r ${ANDROID_PRODUCT_OUT}/obj/ETC/system_build_prop_intermediates ||
+ true
+ pushd ${ANDROID_BUILD_TOP} >&2
+ make -j50 >&2
+ if [ ${?} != 0 ]; then
+ popd >&2
+ return 1
+ fi
+ if ! inFastboot; then
+ adb reboot-bootloader >&2
+ fi
+ fastboot flashall >&2
+ popd >&2
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason "\(reboot,ota\|bootloader\)"
+ EXPECT_PROPERTY persist.sys.boot.reason bootloader
+ report_bootstat_logs reboot,ota bootloader
+}
+
+[ "USAGE: test_optional_ota
+
+fast and fake (touch build_date on device to make it different)" ]
+test_optional_ota() {
+ checkDebugBuild || return
+ duration_test
+ adb shell su root touch /data/misc/bootstat/build_date >&2
+ adb reboot ota
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason reboot,ota
+ EXPECT_PROPERTY persist.sys.boot.reason reboot,ota
+ report_bootstat_logs reboot,ota
+}
+
+[ "USAGE: [TEST=<test>] blind_reboot_test
+
+Simple tests helper
+- adb reboot <test>
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report <test>, or reboot,<test> depending on canonical rules
+
+We interleave the simple reboot tests between the hard/complex ones
+as a means of checking sanity and any persistent side effect of the
+other tests." ]
+blind_reboot_test() {
+ duration_test
+ case ${TEST} in
+ bootloader | recovery | cold | hard | warm ) reason=${TEST} ;;
+ *) reason=reboot,${TEST} ;;
+ esac
+ adb reboot ${TEST}
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason ${reason}
+ EXPECT_PROPERTY persist.sys.boot.reason ${reason}
+ report_bootstat_logs ${reason}
+}
+
+[ "USAGE: test_cold
+
+cold test
+- adb reboot cold
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report cold" ]
+test_cold() {
+ blind_reboot_test
+}
+
+[ "USAGE: test_factory_reset
+
+factory_reset test
+- adb shell su root rm /data/misc/bootstat/build_date
+- adb reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report factory_reset
+
+Decision to rummage through bootstat data files was made as
+a _real_ factory_reset is too destructive to the device." ]
+test_factory_reset() {
+ checkDebugBuild || return
+ duration_test
+ adb shell su root rm /data/misc/bootstat/build_date >&2
+ adb reboot >&2
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
+ EXPECT_PROPERTY persist.sys.boot.reason "reboot,.*"
+ report_bootstat_logs reboot,factory_reset reboot, reboot,adb \
+ "-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory" \
+ "-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date"
+}
+
+[ "USAGE: test_optional_factory_reset
+
+factory_reset test
+- adb reboot-bootloader
+- fastboot format userdata
+- fastboot reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report factory_reset
+
+For realz, and disruptive" ]
+test_optional_factory_reset() {
+ duration_test 60
+ if ! inFastboot; then
+ adb reboot-bootloader
+ fi
+ fastboot format userdata >&2
+ fastboot reboot >&2
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason reboot,factory_reset
+ EXPECT_PROPERTY persist.sys.boot.reason ""
+ report_bootstat_logs reboot,factory_reset bootloader \
+ "-bootstat: Failed to read /data/misc/bootstat/last_boot_time_utc: No such file or directory" \
+ "-bootstat: Failed to parse boot time record: /data/misc/bootstat/last_boot_time_utc" \
+ "-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory" \
+ "-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date" \
+ "-bootstat: Failed to read /data/misc/bootstat/factory_reset: No such file or directory" \
+ "-bootstat: Failed to parse boot time record: /data/misc/bootstat/factory_reset"
+}
+
+[ "USAGE: test_hard
+
+hard test:
+- adb reboot hard
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report hard" ]
+test_hard() {
+ blind_reboot_test
+}
+
+[ "USAGE: test_battery
+
+battery test (trick):
+- echo healthd: battery l=2<space> | adb shell su root tee /dev/kmsg
+- adb reboot cold
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,battery, unless healthd managed to log
+ before reboot in above trick.
+
+- Bonus points (manual extras)
+- Make sure the following is added to the /init.rc file in post-fs
+ section before logd is started:
+ + setprop logd.kernel false
+ + rm /sys/fs/pstore/console-ramoops
+ + rm /sys/fs/pstore/console-ramoops-0
+ + write /dev/kmsg \"healthd: battery l=2${SPACE}
+ +\"
+- adb reboot fs
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,battery
+- (replace set logd.kernel true to the above, and retry test)" ]
+test_battery() {
+ checkDebugBuild || return
+ duration_test 120
+ enterPstore
+ # Send it _many_ times to combat devices with flakey pstore
+ for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do
+ echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null
+ done
+ adb reboot cold >&2
+ adb wait-for-device
+ wait_for_screen
+ adb shell su root \
+ cat /proc/fs/pstore/console-ramoops \
+ /proc/fs/pstore/console-ramoops-0 2>/dev/null |
+ grep 'healthd: battery l=' |
+ tail -1 |
+ grep 'healthd: battery l=2 ' >/dev/null || (
+ if ! EXPECT_PROPERTY sys.boot.reason reboot,battery >/dev/null 2>/dev/null; then
+ # retry
+ for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do
+ echo 'healthd: battery l=2 ' | adb shell su root tee /dev/kmsg >/dev/null
+ done
+ adb reboot cold >&2
+ adb wait-for-device
+ wait_for_screen
+ fi
+ )
+
+ EXPECT_PROPERTY sys.boot.reason shutdown,battery
+ EXPECT_PROPERTY persist.sys.boot.reason cold
+ report_bootstat_logs shutdown,battery "-bootstat: Battery level at shutdown 2%"
+ exitPstore
+}
+
+[ "USAGE: test_optional_battery
+
+battery shutdown test:
+- adb shell setprop sys.powerctl shutdown,battery
+- (power up the device)
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report shutdown,battery" ]
+test_optional_battery() {
+ duration_test ">60"
+ echo " power on request" >&2
+ adb shell setprop sys.powerctl shutdown,battery
+ sleep 5
+ echo -n "WARNING: Please power device back up, waiting ... " >&2
+ wait_for_screen -n >&2
+ EXPECT_PROPERTY sys.boot.reason shutdown,battery
+ EXPECT_PROPERTY persist.sys.boot.reason shutdown,battery
+ report_bootstat_logs shutdown,battery
+}
+
+[ "USAGE: test_optional_battery_thermal
+
+battery thermal shutdown test:
+- adb shell setprop sys.powerctl shutdown,thermal,battery
+- (power up the device)
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report shutdown,thermal,battery" ]
+test_optional_battery_thermal() {
+ duration_test ">60"
+ echo " power on request" >&2
+ adb shell setprop sys.powerctl shutdown,thermal,battery
+ sleep 5
+ echo -n "WARNING: Please power device back up, waiting ... " >&2
+ wait_for_screen -n >&2
+ EXPECT_PROPERTY sys.boot.reason shutdown,thermal,battery
+ EXPECT_PROPERTY persist.sys.boot.reason shutdown,thermal,battery
+ report_bootstat_logs shutdown,thermal,battery
+}
+
+[ "USAGE: test_unknown
+
+unknown test
+- adb reboot unknown
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,unknown
+- NB: expect log \"... I bootstat: Unknown boot reason: reboot,unknown\"" ]
+test_unknown() {
+ blind_reboot_test
+}
+
+[ "USAGE: test_kernel_panic
+
+kernel_panic test:
+- echo c | adb shell su root tee /proc/sysrq-trigger
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report kernel_panic,sysrq" ]
+test_kernel_panic() {
+ checkDebugBuild || return
+ duration_test ">90"
+ panic_msg="kernel_panic,sysrq"
+ enterPstore || panic_msg="\(kernel_panic,sysrq\|kernel_panic\)"
+ echo c | adb shell su root tee /proc/sysrq-trigger >/dev/null
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason ${panic_msg}
+ EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
+ report_bootstat_logs kernel_panic,sysrq
+ exitPstore
+}
+
+[ "USAGE: test_warm
+
+warm test
+- adb reboot warm
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report warm" ]
+test_warm() {
+ blind_reboot_test
+}
+
+[ "USAGE: test_thermal_shutdown
+
+thermal shutdown test:
+- adb shell setprop sys.powerctl shutdown,thermal
+- (power up the device)
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report shutdown,thermal" ]
+test_thermal_shutdown() {
+ duration_test ">60"
+ echo " power on request" >&2
+ adb shell setprop sys.powerctl shutdown,thermal
+ sleep 5
+ echo -n "WARNING: Please power device back up, waiting ... " >&2
+ wait_for_screen -n >&2
+ EXPECT_PROPERTY sys.boot.reason shutdown,thermal
+ EXPECT_PROPERTY persist.sys.boot.reason shutdown,thermal
+ report_bootstat_logs shutdown,thermal
+}
+
+[ "USAGE: test_userrequested_shutdown
+
+userrequested shutdown test:
+- adb shell setprop sys.powerctl shutdown,userrequested
+- (power up the device)
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report shutdown,userrequested" ]
+test_userrequested_shutdown() {
+ duration_test ">60"
+ echo " power on request" >&2
+ adb shell setprop sys.powerctl shutdown,userrequested
+ sleep 5
+ echo -n "WARNING: Please power device back up, waiting ... " >&2
+ wait_for_screen -n >&2
+ EXPECT_PROPERTY sys.boot.reason shutdown,userrequested
+ EXPECT_PROPERTY persist.sys.boot.reason shutdown,userrequested
+ report_bootstat_logs shutdown,userrequested
+}
+
+[ "USAGE: test_shell_reboot
+
+shell reboot test:
+- adb shell reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,shell" ]
+test_shell_reboot() {
+ duration_test
+ adb shell reboot
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason reboot,shell
+ EXPECT_PROPERTY persist.sys.boot.reason reboot,shell
+ report_bootstat_logs reboot,shell
+}
+
+[ "USAGE: test_adb_reboot
+
+adb reboot test:
+- adb reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,adb" ]
+test_adb_reboot() {
+ duration_test
+ adb reboot
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason reboot,adb
+ EXPECT_PROPERTY persist.sys.boot.reason reboot,adb
+ report_bootstat_logs reboot,adb
+}
+
+[ "USAGE: test_Its_Just_So_Hard_reboot
+
+Its Just So Hard reboot test:
+- adb shell reboot 'Its Just So Hard'
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,its_just_so_hard
+- NB: expect log \"... I bootstat: Unknown boot reason: reboot,its_just_so_hard\"" ]
+test_Its_Just_So_Hard_reboot() {
+ duration_test
+ adb shell 'reboot "Its Just So Hard"'
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard
+ EXPECT_PROPERTY persist.sys.boot.reason "reboot,Its Just So Hard"
+ adb shell su root setprop persist.sys.boot.reason reboot,its_just_so_hard
+ if checkDebugBuild; then
+ flag=""
+ else
+ flag="--allow_failure"
+ fi
+ EXPECT_PROPERTY persist.sys.boot.reason reboot,its_just_so_hard ${flag}
+ report_bootstat_logs reboot,its_just_so_hard
+}
+
+[ "USAGE: run_bootloader [value [expected]]
+
+bootloader boot reason injection tests:
+- setBootloaderBootReason value
+- adb shell reboot
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report reboot,value" ]
+run_bootloader() {
+ bootloader_expected="${1}"
+ if [ -z "${bootloader_expected}" ]; then
+ bootloader_expected="${TEST#bootloader_}"
+ fi
+ if ! setBootloaderBootReason ${bootloader_expected}; then
+ echo " Skipping FAILURE." 2>&1
+ return
+ fi
+ duration_test
+ if [ X"warm" = X"${bootloader_expected}" ]; then
+ last_expected=cold
+ else
+ last_expected=warm
+ fi
+ adb reboot ${last_expected}
+ wait_for_screen
+ # Reset so that other tests do not get unexpected injection
+ setBootloaderBootReason
+ # Determine the expected values
+ sys_expected="${2}"
+ if [ -z "${sys_expected}" ]; then
+ sys_expected="`validate_reason ${bootloader_expected}`"
+ if [ "reboot" = "${sys_expected}" ]; then
+ sys_expected="${last_expected}"
+ fi
+ else
+ sys_expected=`validate_reason ${sys_expected}`
+ fi
+ case ${sys_expected} in
+ kernel_panic | kernel_panic,* | watchdog | watchdog,* )
+ last_expected=${sys_expected}
+ ;;
+ esac
+ # Check values
+ EXPECT_PROPERTY ro.boot.bootreason "${bootloader_expected}"
+ EXPECT_PROPERTY sys.boot.reason "${sys_expected}"
+ EXPECT_PROPERTY persist.sys.boot.reason "${last_expected}"
+ report_bootstat_logs "${sys_expected}"
+}
+
+[ "USAGE: test_bootloader_<type>
+
+bootloader boot reasons test injection" ]
+test_bootloader_normal() {
+ run_bootloader
+}
+
+test_bootloader_watchdog() {
+ run_bootloader
+}
+
+test_bootloader_kernel_panic() {
+ run_bootloader
+}
+
+test_bootloader_oem_powerkey() {
+ run_bootloader
+}
+
+test_bootloader_wdog_reset() {
+ run_bootloader
+}
+
+test_bootloader_cold() {
+ run_bootloader
+}
+
+test_bootloader_warm() {
+ run_bootloader
+}
+
+test_bootloader_hard() {
+ run_bootloader
+}
+
+test_bootloader_recovery() {
+ run_bootloader
+}
+
+[ "USAGE: ${0##*/} [-s SERIAL] [tests]
+
+Mainline executive to run the above tests" ]
+
+# Rudimentary argument parsing
+
+if [ ${#} -ge 2 -a X"-s" = X"${1}" ]; then
+ export ANDROID_SERIAL="${2}"
+ shift 2
+fi
+
+if [ X"--help" = X"${1}" -o X"-h" = X"${1}" -o X"-?" = X"${1}" ]; then
+ echo "USAGE: ${0##*/} [-s SERIAL] [tests]"
+ echo tests - `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
+ exit 0
+fi
+
+# Check if all conditions for the script are sane
+
+if [ -z "${ANDROID_SERIAL}" ]; then
+ ndev=`(
+ adb devices | grep -v 'List of devices attached'
+ fastboot devices
+ ) |
+ grep -v "^[${SPACE}${TAB}]*\$" |
+ wc -l`
+ if [ ${ndev} -gt 1 ]; then
+ echo "ERROR: no target device specified, ${ndev} connected" >&2
+ echo "${RED}[ FAILED ]${NORMAL}"
+ exit 1
+ fi
+ echo "WARNING: no target device specified" >&2
+fi
+
+ret=0
+
+# Test Series
+if [ X"all" = X"${*}" ]; then
+ # automagically pick up all test_<function>s.
+ eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null`
+ if [ X"nothing" = X"${1}" ]; then
+ shift 1
+ fi
+fi
+if [ -z "$*" ]; then
+ # automagically pick up all test_<function>, except test_optional_<function>.
+ eval set nothing `sed -n 's/^test_\([^ ()]*\)() {/\1/p' $0 </dev/null |
+ grep -v '^optional_'`
+ if [ -z "${2}" ]; then
+ # Hard coded should shell fail to find them above (search/permission issues)
+ eval set properties ota cold factory_reset hard battery unknown \
+ kernel_panic warm thermal_shutdown userrequested_shutdown \
+ shell_reboot adb_reboot Its_Just_So_Hard_reboot \
+ bootloader_normal bootloader_watchdog bootloader_kernel_panic \
+ bootloader_oem_powerkey bootloader_wdog_reset \
+ bootloader_wdog_reset bootloader_wdog_reset bootloader_hard \
+ bootloader_recovery
+ fi
+ if [ X"nothing" = X"${1}" ]; then
+ shift 1
+ fi
+fi
+echo "INFO: selected test(s): ${@}" >&2
+echo
+# Prepare device
+setBootloaderBootReason 2>/dev/null
+# Start pouring through the tests.
+failures=
+successes=
+for t in "${@}"; do
+ wrap_test ${t}
+ retval=${?}
+ if [ 0 = ${retval} ]; then
+ if [ -z "${successes}" ]; then
+ successes=${t}
+ else
+ successes="${successes} ${t}"
+ fi
+ else
+ ret=${retval}
+ if [ -z "${failures}" ]; then
+ failures=${t}
+ else
+ failures="${failures} ${t}"
+ fi
+ fi
+ echo
+done
+
+if [ -n "${successes}" ]; then
+ echo "${GREEN}[ PASSED ]${NORMAL} ${successes}"
+fi
+if [ -n "${failures}" ]; then
+ echo "${RED}[ FAILED ]${NORMAL} ${failures}"
+fi
+exit ${ret}
diff --git a/bootstat/bootstat-debug.rc b/bootstat/bootstat-debug.rc
new file mode 100644
index 0000000..6a00440
--- /dev/null
+++ b/bootstat/bootstat-debug.rc
@@ -0,0 +1,7 @@
+# This file is the userdebug LOCAL_INIT_RC file for the bootstat command.
+
+# FOR TESTING
+# For devices w/o bootloader boot reason reported, mirror test boot reason
+# to bootloader boot reason to allow test to inject reasons
+on property:persist.test.boot.reason=*
+ setprop ro.boot.bootreason ${persist.test.boot.reason}
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index bd611f0..f8eaa1c 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -19,6 +19,7 @@
// uploaded to Android log storage via Tron.
#include <getopt.h>
+#include <sys/klog.h>
#include <unistd.h>
#include <chrono>
@@ -32,11 +33,14 @@
#include <vector>
#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <android/log.h>
+#include <cutils/android_reboot.h>
#include <cutils/properties.h>
+#include <log/logcat.h>
#include <metricslogger/metrics_logger.h>
#include "boot_event_record_store.h"
@@ -57,8 +61,7 @@
// Records the named boot |event| to the record store. If |value| is non-empty
// and is a proper string representation of an integer value, the converted
// integer value is associated with the boot event.
-void RecordBootEventFromCommandLine(
- const std::string& event, const std::string& value_str) {
+void RecordBootEventFromCommandLine(const std::string& event, const std::string& value_str) {
BootEventRecordStore boot_event_store;
if (!value_str.empty()) {
int32_t value = 0;
@@ -81,7 +84,7 @@
}
}
-void ShowHelp(const char *cmd) {
+void ShowHelp(const char* cmd) {
fprintf(stderr, "Usage: %s [options]\n", cmd);
fprintf(stderr,
"options include:\n"
@@ -97,7 +100,7 @@
// Constructs a readable, printable string from the givencommand line
// arguments.
-std::string GetCommandLine(int argc, char **argv) {
+std::string GetCommandLine(int argc, char** argv) {
std::string cmd;
for (int i = 0; i < argc; ++i) {
cmd += argv[i];
@@ -118,6 +121,15 @@
return std::string(&temp[0], len);
}
+void SetProperty(const char* key, const std::string& val) {
+ property_set(key, val.c_str());
+}
+
+void SetProperty(const char* key, const char* val) {
+ property_set(key, val);
+}
+
+constexpr int32_t kEmptyBootReason = 0;
constexpr int32_t kUnknownBootReason = 1;
// A mapping from boot reason string, as read from the ro.boot.bootreason
@@ -125,57 +137,81 @@
// the boot_reason metric may refer to this mapping to discern the histogram
// values.
const std::map<std::string, int32_t> kBootReasonMap = {
- {"unknown", kUnknownBootReason},
- {"normal", 2},
- {"recovery", 3},
- {"reboot", 4},
- {"PowerKey", 5},
- {"hard_reset", 6},
- {"kernel_panic", 7},
- {"rpm_err", 8},
- {"hw_reset", 9},
- {"tz_err", 10},
- {"adsp_err", 11},
- {"modem_err", 12},
- {"mba_err", 13},
- {"Watchdog", 14},
- {"Panic", 15},
- {"power_key", 16},
- {"power_on", 17},
- {"Reboot", 18},
- {"rtc", 19},
- {"edl", 20},
- {"oem_pon1", 21},
- {"oem_powerkey", 22},
- {"oem_unknown_reset", 23},
- {"srto: HWWDT reset SC", 24},
- {"srto: HWWDT reset platform", 25},
- {"srto: bootloader", 26},
- {"srto: kernel panic", 27},
- {"srto: kernel watchdog reset", 28},
- {"srto: normal", 29},
- {"srto: reboot", 30},
- {"srto: reboot-bootloader", 31},
- {"srto: security watchdog reset", 32},
- {"srto: wakesrc", 33},
- {"srto: watchdog", 34},
- {"srto:1-1", 35},
- {"srto:omap_hsmm", 36},
- {"srto:phy0", 37},
- {"srto:rtc0", 38},
- {"srto:touchpad", 39},
- {"watchdog", 40},
- {"watchdogr", 41},
- {"wdog_bark", 42},
- {"wdog_bite", 43},
- {"wdog_reset", 44},
- {"shutdown,", 45}, // Trailing comma is intentional.
- {"shutdown,userrequested", 46},
- {"reboot,bootloader", 47},
- {"reboot,cold", 48},
- {"reboot,recovery", 49},
- {"thermal_shutdown", 50},
- {"s3_wakeup", 51}
+ {"empty", kEmptyBootReason},
+ {"unknown", kUnknownBootReason},
+ {"normal", 2},
+ {"recovery", 3},
+ {"reboot", 4},
+ {"PowerKey", 5},
+ {"hard_reset", 6},
+ {"kernel_panic", 7},
+ {"rpm_err", 8},
+ {"hw_reset", 9},
+ {"tz_err", 10},
+ {"adsp_err", 11},
+ {"modem_err", 12},
+ {"mba_err", 13},
+ {"Watchdog", 14},
+ {"Panic", 15},
+ {"power_key", 16},
+ {"power_on", 17},
+ {"Reboot", 18},
+ {"rtc", 19},
+ {"edl", 20},
+ {"oem_pon1", 21},
+ {"oem_powerkey", 22},
+ {"oem_unknown_reset", 23},
+ {"srto: HWWDT reset SC", 24},
+ {"srto: HWWDT reset platform", 25},
+ {"srto: bootloader", 26},
+ {"srto: kernel panic", 27},
+ {"srto: kernel watchdog reset", 28},
+ {"srto: normal", 29},
+ {"srto: reboot", 30},
+ {"srto: reboot-bootloader", 31},
+ {"srto: security watchdog reset", 32},
+ {"srto: wakesrc", 33},
+ {"srto: watchdog", 34},
+ {"srto:1-1", 35},
+ {"srto:omap_hsmm", 36},
+ {"srto:phy0", 37},
+ {"srto:rtc0", 38},
+ {"srto:touchpad", 39},
+ {"watchdog", 40},
+ {"watchdogr", 41},
+ {"wdog_bark", 42},
+ {"wdog_bite", 43},
+ {"wdog_reset", 44},
+ {"shutdown,", 45}, // Trailing comma is intentional.
+ {"shutdown,userrequested", 46},
+ {"reboot,bootloader", 47},
+ {"reboot,cold", 48},
+ {"reboot,recovery", 49},
+ {"thermal_shutdown", 50},
+ {"s3_wakeup", 51},
+ {"kernel_panic,sysrq", 52},
+ {"kernel_panic,NULL", 53},
+ {"kernel_panic,BUG", 54},
+ {"bootloader", 55},
+ {"cold", 56},
+ {"hard", 57},
+ {"warm", 58},
+ {"recovery", 59},
+ {"thermal-shutdown", 60},
+ {"shutdown,thermal", 61},
+ {"shutdown,battery", 62},
+ {"reboot,ota", 63},
+ {"reboot,factory_reset", 64},
+ {"reboot,", 65},
+ {"reboot,shell", 66},
+ {"reboot,adb", 67},
+ {"reboot,userrequested", 68},
+ {"shutdown,container", 69}, // Host OS asking Android Container to shutdown
+ {"cold,powerkey", 70},
+ {"warm,s3_wakeup", 71},
+ {"hard,hw_reset", 72},
+ {"shutdown,suspend", 73}, // Suspend to RAM
+ {"shutdown,hibernate", 74}, // Suspend to DISK
};
// Converts a string value representing the reason the system booted to an
@@ -187,10 +223,373 @@
return mapping->second;
}
+ if (boot_reason.empty()) {
+ return kEmptyBootReason;
+ }
+
LOG(INFO) << "Unknown boot reason: " << boot_reason;
return kUnknownBootReason;
}
+// Canonical list of supported primary reboot reasons.
+const std::vector<const std::string> knownReasons = {
+ // clang-format off
+ // kernel
+ "watchdog",
+ "kernel_panic",
+ // strong
+ "recovery", // Should not happen from ro.boot.bootreason
+ "bootloader", // Should not happen from ro.boot.bootreason
+ // blunt
+ "cold",
+ "hard",
+ "warm",
+ // super blunt
+ "shutdown", // Can not happen from ro.boot.bootreason
+ "reboot", // Default catch-all for anything unknown
+ // clang-format on
+};
+
+// Returns true if the supplied reason prefix is considered detailed enough.
+bool isStrongRebootReason(const std::string& r) {
+ for (auto& s : knownReasons) {
+ if (s == "cold") break;
+ // Prefix defined as terminated by a nul or comma (,).
+ if (android::base::StartsWith(r, s.c_str()) &&
+ ((r.length() == s.length()) || (r[s.length()] == ','))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Returns true if the supplied reason prefix is associated with the kernel.
+bool isKernelRebootReason(const std::string& r) {
+ for (auto& s : knownReasons) {
+ if (s == "recovery") break;
+ // Prefix defined as terminated by a nul or comma (,).
+ if (android::base::StartsWith(r, s.c_str()) &&
+ ((r.length() == s.length()) || (r[s.length()] == ','))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Returns true if the supplied reason prefix is considered known.
+bool isKnownRebootReason(const std::string& r) {
+ for (auto& s : knownReasons) {
+ // Prefix defined as terminated by a nul or comma (,).
+ if (android::base::StartsWith(r, s.c_str()) &&
+ ((r.length() == s.length()) || (r[s.length()] == ','))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// If the reboot reason should be improved, report true if is too blunt.
+bool isBluntRebootReason(const std::string& r) {
+ if (isStrongRebootReason(r)) return false;
+
+ if (!isKnownRebootReason(r)) return true; // Can not support unknown as detail
+
+ size_t pos = 0;
+ while ((pos = r.find(',', pos)) != std::string::npos) {
+ ++pos;
+ std::string next(r.substr(pos));
+ if (next.length() == 0) break;
+ if (next[0] == ',') continue;
+ if (!isKnownRebootReason(next)) return false; // Unknown subreason is good.
+ if (isStrongRebootReason(next)) return false; // eg: reboot,reboot
+ }
+ return true;
+}
+
+bool readPstoreConsole(std::string& console) {
+ if (android::base::ReadFileToString("/sys/fs/pstore/console-ramoops-0", &console)) {
+ return true;
+ }
+ return android::base::ReadFileToString("/sys/fs/pstore/console-ramoops", &console);
+}
+
+bool addKernelPanicSubReason(const std::string& console, std::string& ret) {
+ // Check for kernel panic types to refine information
+ if (console.rfind("SysRq : Trigger a crash") != std::string::npos) {
+ // Can not happen, except on userdebug, during testing/debugging.
+ ret = "kernel_panic,sysrq";
+ return true;
+ }
+ if (console.rfind("Unable to handle kernel NULL pointer dereference at virtual address") !=
+ std::string::npos) {
+ ret = "kernel_panic,NULL";
+ return true;
+ }
+ if (console.rfind("Kernel BUG at ") != std::string::npos) {
+ ret = "kernel_panic,BUG";
+ return true;
+ }
+ return false;
+}
+
+// std::transform Helper callback functions:
+// Converts a string value representing the reason the system booted to a
+// string complying with Android system standard reason.
+char tounderline(char c) {
+ return ::isblank(c) ? '_' : c;
+}
+char toprintable(char c) {
+ return ::isprint(c) ? c : '?';
+}
+
+const char system_reboot_reason_property[] = "sys.boot.reason";
+const char last_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY;
+const char bootloader_reboot_reason_property[] = "ro.boot.bootreason";
+
+// Scrub, Sanitize, Standardize and Enhance the boot reason string supplied.
+std::string BootReasonStrToReason(const std::string& boot_reason) {
+ static const size_t max_reason_length = 256;
+
+ std::string ret(GetProperty(system_reboot_reason_property));
+ std::string reason(boot_reason);
+ // If sys.boot.reason == ro.boot.bootreason, let's re-evaluate
+ if (reason == ret) ret = "";
+
+ // Cleanup boot_reason regarding acceptable character set
+ std::transform(reason.begin(), reason.end(), reason.begin(), ::tolower);
+ std::transform(reason.begin(), reason.end(), reason.begin(), tounderline);
+ std::transform(reason.begin(), reason.end(), reason.begin(), toprintable);
+
+ // Is the current system boot reason sys.boot.reason valid?
+ if (!isKnownRebootReason(ret)) ret = "";
+
+ if (ret == "") {
+ // Is the bootloader boot reason ro.boot.bootreason known?
+ std::vector<std::string> words(android::base::Split(reason, ",_-"));
+ for (auto& s : knownReasons) {
+ std::string blunt;
+ for (auto& r : words) {
+ if (r == s) {
+ if (isBluntRebootReason(s)) {
+ blunt = s;
+ } else {
+ ret = s;
+ break;
+ }
+ }
+ }
+ if (ret == "") ret = blunt;
+ if (ret != "") break;
+ }
+ }
+
+ if (ret == "") {
+ // A series of checks to take some officially unsupported reasons
+ // reported by the bootloader and find some logical and canonical
+ // sense. In an ideal world, we would require those bootloaders
+ // to behave and follow our standards.
+ static const std::vector<std::pair<const std::string, const std::string>> aliasReasons = {
+ {"watchdog", "wdog"},
+ {"cold,powerkey", "powerkey"},
+ {"kernel_panic", "panic"},
+ {"shutdown,thermal", "thermal"},
+ {"warm,s3_wakeup", "s3_wakeup"},
+ {"hard,hw_reset", "hw_reset"},
+ {"bootloader", ""},
+ };
+
+ // Either the primary or alias is found _somewhere_ in the reason string.
+ for (auto& s : aliasReasons) {
+ if (reason.find(s.first) != std::string::npos) {
+ ret = s.first;
+ break;
+ }
+ if (s.second.size() && (reason.find(s.second) != std::string::npos)) {
+ ret = s.first;
+ break;
+ }
+ }
+ }
+
+ // If watchdog is the reason, see if there is a security angle?
+ if (ret == "watchdog") {
+ if (reason.find("sec") != std::string::npos) {
+ ret += ",security";
+ }
+ }
+
+ if (ret == "kernel_panic") {
+ // Check to see if last klog has some refinement hints.
+ std::string content;
+ if (readPstoreConsole(content)) {
+ addKernelPanicSubReason(content, ret);
+ }
+ } else if (isBluntRebootReason(ret)) {
+ // Check the other available reason resources if the reason is still blunt.
+
+ // Check to see if last klog has some refinement hints.
+ std::string content;
+ if (readPstoreConsole(content)) {
+ // The toybox reboot command used directly (unlikely)? But also
+ // catches init's response to Android's more controlled reboot command.
+ if (content.rfind("reboot: Power down") != std::string::npos) {
+ ret = "shutdown"; // Still too blunt, but more accurate.
+ // ToDo: init should record the shutdown reason to kernel messages ala:
+ // init: shutdown system with command 'last_reboot_reason'
+ // so that if pstore has persistence we can get some details
+ // that could be missing in last_reboot_reason_property.
+ }
+
+ static const char cmd[] = "reboot: Restarting system with command '";
+ size_t pos = content.rfind(cmd);
+ if (pos != std::string::npos) {
+ pos += strlen(cmd);
+ std::string subReason(content.substr(pos, max_reason_length));
+ for (pos = 0; pos < subReason.length(); ++pos) {
+ char c = tounderline(subReason[pos]);
+ if (!::isprint(c) || (c == '\'')) {
+ subReason.erase(pos);
+ break;
+ }
+ subReason[pos] = ::tolower(c);
+ }
+ if (subReason != "") { // Will not land "reboot" as that is too blunt.
+ if (isKernelRebootReason(subReason)) {
+ ret = "reboot," + subReason; // User space can't talk kernel reasons.
+ } else {
+ ret = subReason;
+ }
+ }
+ }
+
+ // Check for kernel panics, allowed to override reboot command.
+ if (!addKernelPanicSubReason(content, ret) &&
+ // check for long-press power down
+ ((content.rfind("Power held for ") != std::string::npos) ||
+ (content.rfind("charger: [") != std::string::npos))) {
+ ret = "cold";
+ }
+ }
+
+ // The following battery test should migrate to a default system health HAL
+
+ // Let us not worry if the reboot command was issued, for the cases of
+ // reboot -p, reboot <no reason>, reboot cold, reboot warm and reboot hard.
+ // Same for bootloader and ro.boot.bootreasons of this set, but a dead
+ // battery could conceivably lead to these, so worthy of override.
+ if (isBluntRebootReason(ret)) {
+ // Heuristic to determine if shutdown possibly because of a dead battery?
+ // Really a hail-mary pass to find it in last klog content ...
+ static const int battery_dead_threshold = 2; // percent
+ static const char battery[] = "healthd: battery l=";
+ size_t pos = content.rfind(battery); // last one
+ std::string digits;
+ if (pos != std::string::npos) {
+ digits = content.substr(pos + strlen(battery));
+ }
+ char* endptr = NULL;
+ unsigned long long level = strtoull(digits.c_str(), &endptr, 10);
+ if ((level <= 100) && (endptr != digits.c_str()) && (*endptr == ' ')) {
+ LOG(INFO) << "Battery level at shutdown " << level << "%";
+ if (level <= battery_dead_threshold) {
+ ret = "shutdown,battery";
+ }
+ } else { // Most likely
+ digits = ""; // reset digits
+
+ // Content buffer no longer will have console data. Beware if more
+ // checks added below, that depend on parsing console content.
+ content = "";
+
+ LOG(DEBUG) << "Can not find last low battery in last console messages";
+ android_logcat_context ctx = create_android_logcat();
+ FILE* fp = android_logcat_popen(&ctx, "logcat -b kernel -v brief -d");
+ if (fp != nullptr) {
+ android::base::ReadFdToString(fileno(fp), &content);
+ }
+ android_logcat_pclose(&ctx, fp);
+ android_logcat_destroy(&ctx);
+ static const char logcat_battery[] = "W/healthd ( 0): battery l=";
+ const char* match = logcat_battery;
+
+ if (content == "") {
+ // Service logd.klog not running, go to smaller buffer in the kernel.
+ int rc = klogctl(KLOG_SIZE_BUFFER, nullptr, 0);
+ if (rc > 0) {
+ ssize_t len = rc + 1024; // 1K Margin should it grow between calls.
+ std::unique_ptr<char[]> buf(new char[len]);
+ rc = klogctl(KLOG_READ_ALL, buf.get(), len);
+ if (rc < len) {
+ len = rc + 1;
+ }
+ buf[--len] = '\0';
+ content = buf.get();
+ }
+ match = battery;
+ }
+
+ pos = content.find(match); // The first one it finds.
+ if (pos != std::string::npos) {
+ digits = content.substr(pos + strlen(match));
+ }
+ endptr = NULL;
+ level = strtoull(digits.c_str(), &endptr, 10);
+ if ((level <= 100) && (endptr != digits.c_str()) && (*endptr == ' ')) {
+ LOG(INFO) << "Battery level at startup " << level << "%";
+ if (level <= battery_dead_threshold) {
+ ret = "shutdown,battery";
+ }
+ } else {
+ LOG(DEBUG) << "Can not find first battery level in dmesg or logcat";
+ }
+ }
+ }
+
+ // Is there a controlled shutdown hint in last_reboot_reason_property?
+ if (isBluntRebootReason(ret)) {
+ // Content buffer no longer will have console data. Beware if more
+ // checks added below, that depend on parsing console content.
+ content = GetProperty(last_reboot_reason_property);
+ // Cleanup last_boot_reason regarding acceptable character set
+ std::transform(content.begin(), content.end(), content.begin(), ::tolower);
+ std::transform(content.begin(), content.end(), content.begin(), tounderline);
+ std::transform(content.begin(), content.end(), content.begin(), toprintable);
+
+ // Anything in last is better than 'super-blunt' reboot or shutdown.
+ if ((ret == "") || (ret == "reboot") || (ret == "shutdown") || !isBluntRebootReason(content)) {
+ ret = content;
+ }
+ }
+
+ // Other System Health HAL reasons?
+
+ // ToDo: /proc/sys/kernel/boot_reason needs a HAL interface to
+ // possibly offer hardware-specific clues from the PMIC.
+ }
+
+ // If unknown left over from above, make it "reboot,<boot_reason>"
+ if (ret == "") {
+ ret = "reboot";
+ if (android::base::StartsWith(reason, "reboot")) {
+ reason = reason.substr(strlen("reboot"));
+ while ((reason[0] == ',') || (reason[0] == '_')) {
+ reason = reason.substr(1);
+ }
+ }
+ if (reason != "") {
+ ret += ",";
+ ret += reason;
+ }
+ }
+
+ LOG(INFO) << "Canonical boot reason: " << ret;
+ if (isKernelRebootReason(ret) && (GetProperty(last_reboot_reason_property) != "")) {
+ // Rewrite as it must be old news, kernel reasons trump user space.
+ SetProperty(last_reboot_reason_property, ret);
+ }
+ return ret;
+}
+
// Returns the appropriate metric key prefix for the boot_complete metric such
// that boot metrics after a system update are labeled as ota_boot_complete;
// otherwise, they are labeled as boot_complete. This method encapsulates the
@@ -212,17 +611,20 @@
if (!boot_event_store.GetBootEvent(kBuildDateKey, &record)) {
boot_complete_prefix = "factory_reset_" + boot_complete_prefix;
boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
+ LOG(INFO) << "Canonical boot reason: reboot,factory_reset";
+ SetProperty(system_reboot_reason_property, "reboot,factory_reset");
} else if (build_date != record.second) {
boot_complete_prefix = "ota_" + boot_complete_prefix;
boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);
+ LOG(INFO) << "Canonical boot reason: reboot,ota";
+ SetProperty(system_reboot_reason_property, "reboot,ota");
}
return boot_complete_prefix;
}
// Records the value of a given ro.boottime.init property in milliseconds.
-void RecordInitBootTimeProp(
- BootEventRecordStore* boot_event_store, const char* property) {
+void RecordInitBootTimeProp(BootEventRecordStore* boot_event_store, const char* property) {
std::string value = GetProperty(property);
int32_t time_in_ms;
@@ -307,10 +709,8 @@
if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) {
time_t last_boot_time_utc = record.second;
- time_t time_since_last_boot = difftime(current_time_utc,
- last_boot_time_utc);
- boot_event_store.AddBootEventWithValue("time_since_last_boot",
- time_since_last_boot);
+ time_t time_since_last_boot = difftime(current_time_utc, last_boot_time_utc);
+ boot_event_store.AddBootEventWithValue("time_since_last_boot", time_since_last_boot);
}
boot_event_store.AddBootEventWithValue("last_boot_time_utc", current_time_utc);
@@ -336,8 +736,7 @@
boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_post_decrypt",
boot_complete.count());
} else {
- boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption",
- uptime.count());
+ boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption", uptime.count());
}
// Record the total time from device startup to boot complete, regardless of
@@ -358,9 +757,33 @@
// Records the boot_reason metric by querying the ro.boot.bootreason system
// property.
void RecordBootReason() {
- int32_t boot_reason = BootReasonStrToEnum(GetProperty("ro.boot.bootreason"));
+ const std::string reason(GetProperty(bootloader_reboot_reason_property));
+
+ if (reason.empty()) {
+ // Log an empty boot reason value as '<EMPTY>' to ensure the value is intentional
+ // (and not corruption anywhere else in the reporting pipeline).
+ android::metricslogger::LogMultiAction(android::metricslogger::ACTION_BOOT,
+ android::metricslogger::FIELD_PLATFORM_REASON, "<EMPTY>");
+ } else {
+ android::metricslogger::LogMultiAction(android::metricslogger::ACTION_BOOT,
+ android::metricslogger::FIELD_PLATFORM_REASON, reason);
+ }
+
+ // Log the raw bootloader_boot_reason property value.
+ int32_t boot_reason = BootReasonStrToEnum(reason);
BootEventRecordStore boot_event_store;
boot_event_store.AddBootEventWithValue("boot_reason", boot_reason);
+
+ // Log the scrubbed system_boot_reason.
+ const std::string system_reason(BootReasonStrToReason(reason));
+ int32_t system_boot_reason = BootReasonStrToEnum(system_reason);
+ boot_event_store.AddBootEventWithValue("system_boot_reason", system_boot_reason);
+
+ // Record the scrubbed system_boot_reason to the property
+ SetProperty(system_reboot_reason_property, system_reason);
+ if (reason == "") {
+ SetProperty(bootloader_reboot_reason_property, system_reason);
+ }
}
// Records two metrics related to the user resetting a device: the time at
@@ -374,21 +797,20 @@
if (current_time_utc < 0) {
// UMA does not display negative values in buckets, so convert to positive.
- android::metricslogger::LogHistogram(
- "factory_reset_current_time_failure", std::abs(current_time_utc));
+ android::metricslogger::LogHistogram("factory_reset_current_time_failure",
+ std::abs(current_time_utc));
// Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
// is losing records somehow.
- boot_event_store.AddBootEventWithValue(
- "factory_reset_current_time_failure", std::abs(current_time_utc));
+ boot_event_store.AddBootEventWithValue("factory_reset_current_time_failure",
+ std::abs(current_time_utc));
return;
} else {
android::metricslogger::LogHistogram("factory_reset_current_time", current_time_utc);
// Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
// is losing records somehow.
- boot_event_store.AddBootEventWithValue(
- "factory_reset_current_time", current_time_utc);
+ boot_event_store.AddBootEventWithValue("factory_reset_current_time", current_time_utc);
}
// The factory_reset boot event does not exist after the device is reset, so
@@ -408,18 +830,15 @@
// Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram
// is losing records somehow.
- boot_event_store.AddBootEventWithValue(
- "factory_reset_record_value", factory_reset_utc);
+ boot_event_store.AddBootEventWithValue("factory_reset_record_value", factory_reset_utc);
- time_t time_since_factory_reset = difftime(current_time_utc,
- factory_reset_utc);
- boot_event_store.AddBootEventWithValue("time_since_factory_reset",
- time_since_factory_reset);
+ time_t time_since_factory_reset = difftime(current_time_utc, factory_reset_utc);
+ boot_event_store.AddBootEventWithValue("time_since_factory_reset", time_since_factory_reset);
}
} // namespace
-int main(int argc, char **argv) {
+int main(int argc, char** argv) {
android::base::InitLogging(argv);
const std::string cmd_line = GetCommandLine(argc, argv);
@@ -431,15 +850,17 @@
static const char boot_reason_str[] = "record_boot_reason";
static const char factory_reset_str[] = "record_time_since_factory_reset";
static const struct option long_options[] = {
- { "help", no_argument, NULL, 'h' },
- { "log", no_argument, NULL, 'l' },
- { "print", no_argument, NULL, 'p' },
- { "record", required_argument, NULL, 'r' },
- { value_str, required_argument, NULL, 0 },
- { boot_complete_str, no_argument, NULL, 0 },
- { boot_reason_str, no_argument, NULL, 0 },
- { factory_reset_str, no_argument, NULL, 0 },
- { NULL, 0, NULL, 0 }
+ // clang-format off
+ { "help", no_argument, NULL, 'h' },
+ { "log", no_argument, NULL, 'l' },
+ { "print", no_argument, NULL, 'p' },
+ { "record", required_argument, NULL, 'r' },
+ { value_str, required_argument, NULL, 0 },
+ { boot_complete_str, no_argument, NULL, 0 },
+ { boot_reason_str, no_argument, NULL, 0 },
+ { factory_reset_str, no_argument, NULL, 0 },
+ { NULL, 0, NULL, 0 }
+ // clang-format on
};
std::string boot_event;
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index d697efb..f06a38f 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -1,5 +1,9 @@
# This file is the LOCAL_INIT_RC file for the bootstat command.
+# mirror bootloader boot reason to system boot reason
+on property:ro.boot.bootreason=*
+ setprop sys.boot.reason ${ro.boot.bootreason}
+
on post-fs-data
mkdir /data/misc/bootstat 0700 system log
# To deal with ota transition resulting from a change in DAC from
@@ -8,16 +12,16 @@
chown system log /data/misc/bootstat/boot_complete
chown system log /data/misc/bootstat/boot_complete_no_encryption
chown system log /data/misc/bootstat/boot_reason
- chown system log /data/misc/bootstat/bootime.bootloader.1BLE
- chown system log /data/misc/bootstat/bootime.bootloader.1BLL
- chown system log /data/misc/bootstat/bootime.bootloader.2BLE
- chown system log /data/misc/bootstat/bootime.bootloader.2BLL
- chown system log /data/misc/bootstat/bootime.bootloader.AVB
- chown system log /data/misc/bootstat/bootime.bootloader.KD
- chown system log /data/misc/bootstat/bootime.bootloader.KL
- chown system log /data/misc/bootstat/bootime.bootloader.ODT
- chown system log /data/misc/bootstat/bootime.bootloader.SW
- chown system log /data/misc/bootstat/bootime.bootloader.total
+ chown system log /data/misc/bootstat/boottime.bootloader.1BLE
+ chown system log /data/misc/bootstat/boottime.bootloader.1BLL
+ chown system log /data/misc/bootstat/boottime.bootloader.2BLE
+ chown system log /data/misc/bootstat/boottime.bootloader.2BLL
+ chown system log /data/misc/bootstat/boottime.bootloader.AVB
+ chown system log /data/misc/bootstat/boottime.bootloader.KD
+ chown system log /data/misc/bootstat/boottime.bootloader.KL
+ chown system log /data/misc/bootstat/boottime.bootloader.ODT
+ chown system log /data/misc/bootstat/boottime.bootloader.SW
+ chown system log /data/misc/bootstat/boottime.bootloader.total
chown system log /data/misc/bootstat/build_date
chown system log /data/misc/bootstat/factory_reset
chown system log /data/misc/bootstat/factory_reset_boot_complete
@@ -42,7 +46,7 @@
# property:init.svc.bootanim=running: The boot animation is running
# property:ro.crypto.type=block: FDE device
on post-fs-data && property:init.svc.bootanim=running && property:ro.crypto.type=block
- exec - system log -- /system/bin/bootstat -r post_decrypt_time_elapsed
+ exec_background - system log -- /system/bin/bootstat -r post_decrypt_time_elapsed
# sys.logbootcomplete is a signal to enable the bootstat logging mechanism.
# This signaling is necessary to prevent logging boot metrics after a runtime
@@ -65,13 +69,7 @@
# Record boot complete metrics.
on property:sys.boot_completed=1 && property:sys.logbootcomplete=1
# Record boot_complete and related stats (decryption, etc).
- exec - system log -- /system/bin/bootstat --record_boot_complete
-
# Record the boot reason.
- exec - system log -- /system/bin/bootstat --record_boot_reason
-
# Record time since factory reset.
- exec - system log -- /system/bin/bootstat --record_time_since_factory_reset
-
# Log all boot events.
- exec - system log -- /system/bin/bootstat -l
+ exec_background - system log -- /system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 3513980..6ef3ed6 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -47,13 +47,14 @@
#define ATRACE_TAG ATRACE_TAG_BIONIC
#include <utils/Trace.h>
-#include "backtrace.h"
-#include "tombstone.h"
-#include "utility.h"
+#include "libdebuggerd/backtrace.h"
+#include "libdebuggerd/tombstone.h"
+#include "libdebuggerd/utility.h"
#include "debuggerd/handler.h"
-#include "protocol.h"
#include "tombstoned/tombstoned.h"
+
+#include "protocol.h"
#include "util.h"
using android::base::unique_fd;
@@ -461,14 +462,14 @@
if (wait_for_gdb) {
// Use ALOGI to line up with output from engrave_tombstone.
ALOGI(
- "***********************************************************\n"
- "* Process %d has been suspended while crashing.\n"
- "* To attach gdbserver and start gdb, run this on the host:\n"
- "*\n"
- "* gdbclient.py -p %d\n"
- "*\n"
- "***********************************************************",
- target, main_tid);
+ "***********************************************************\n"
+ "* Process %d has been suspended while crashing.\n"
+ "* To attach gdbserver and start gdb, run this on the host:\n"
+ "*\n"
+ "* gdbclient.py -p %d\n"
+ "*\n"
+ "***********************************************************",
+ target, target);
}
if (fatal_signal) {
diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp
index b7b1938..67b4ab7 100644
--- a/debuggerd/crasher/Android.bp
+++ b/debuggerd/crasher/Android.bp
@@ -17,7 +17,7 @@
arm: {
srcs: ["arm/crashglue.S"],
- armv7_a_neon: {
+ neon: {
asflags: ["-DHAS_VFP_D32"],
},
},
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index f57349b..e9a3ebd 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -114,6 +114,11 @@
return reinterpret_cast<uintptr_t>(result);
}
+noinline int crash_null() {
+ int (*null_func)() = nullptr;
+ return null_func();
+}
+
noinline int crash3(int a) {
*reinterpret_cast<int*>(0xdead) = a;
return a*4;
@@ -169,6 +174,7 @@
fprintf(stderr, " nostack crash with a NULL stack pointer\n");
fprintf(stderr, "\n");
fprintf(stderr, " heap-usage cause a libc abort by abusing a heap function\n");
+ fprintf(stderr, " call-null cause a crash by calling through a nullptr\n");
fprintf(stderr, " leak leak memory until we get OOM-killed\n");
fprintf(stderr, "\n");
fprintf(stderr, " abort call abort()\n");
@@ -239,6 +245,8 @@
crashnostack();
} else if (!strcasecmp(arg, "exit")) {
exit(1);
+ } else if (!strcasecmp(arg, "call-null")) {
+ return crash_null();
} else if (!strcasecmp(arg, "crash") || !strcmp(arg, "SIGSEGV")) {
return crash(42);
} else if (!strcasecmp(arg, "abort")) {
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index dbf81a4..45e768d 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -19,6 +19,7 @@
#include <sys/capability.h>
#include <sys/prctl.h>
#include <sys/ptrace.h>
+#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index 43104ec..06d4a9b 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -45,8 +45,8 @@
#include "tombstoned/tombstoned.h"
#include "util.h"
-#include "backtrace.h"
-#include "tombstone.h"
+#include "libdebuggerd/backtrace.h"
+#include "libdebuggerd/tombstone.h"
using android::base::unique_fd;
diff --git a/debuggerd/libdebuggerd/arm/machine.cpp b/debuggerd/libdebuggerd/arm/machine.cpp
index ac833ae..bfb5ea4 100644
--- a/debuggerd/libdebuggerd/arm/machine.cpp
+++ b/debuggerd/libdebuggerd/arm/machine.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "DEBUG"
+#include "libdebuggerd/machine.h"
+
#include <errno.h>
#include <stdint.h>
#include <string.h>
@@ -25,8 +27,7 @@
#include <backtrace/Backtrace.h>
#include <log/log.h>
-#include "machine.h"
-#include "utility.h"
+#include "libdebuggerd/utility.h"
void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
pt_regs regs;
diff --git a/debuggerd/libdebuggerd/arm64/machine.cpp b/debuggerd/libdebuggerd/arm64/machine.cpp
index fa73c99a..ad1c951 100644
--- a/debuggerd/libdebuggerd/arm64/machine.cpp
+++ b/debuggerd/libdebuggerd/arm64/machine.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "DEBUG"
+#include "libdebuggerd/machine.h"
+
#include <elf.h>
#include <errno.h>
#include <stdint.h>
@@ -27,8 +29,7 @@
#include <backtrace/Backtrace.h>
#include <log/log.h>
-#include "machine.h"
-#include "utility.h"
+#include "libdebuggerd/utility.h"
void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
struct user_pt_regs regs;
diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp
index 334d97f..f616e1b 100644
--- a/debuggerd/libdebuggerd/backtrace.cpp
+++ b/debuggerd/libdebuggerd/backtrace.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "DEBUG"
+#include "libdebuggerd/backtrace.h"
+
#include <errno.h>
#include <dirent.h>
#include <limits.h>
@@ -34,9 +36,7 @@
#include <backtrace/Backtrace.h>
#include <log/log.h>
-#include "backtrace.h"
-
-#include "utility.h"
+#include "libdebuggerd/utility.h"
static void dump_process_header(log_t* log, pid_t pid, const char* process_name) {
time_t t = time(NULL);
diff --git a/debuggerd/libdebuggerd/elf_utils.cpp b/debuggerd/libdebuggerd/elf_utils.cpp
index 4e798e2..a35102f 100644
--- a/debuggerd/libdebuggerd/elf_utils.cpp
+++ b/debuggerd/libdebuggerd/elf_utils.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "DEBUG"
+#include "libdebuggerd/elf_utils.h"
+
#include <elf.h>
#include <stdint.h>
#include <stdlib.h>
@@ -27,8 +29,6 @@
#include <backtrace/Backtrace.h>
#include <log/log.h>
-#include "elf_utils.h"
-
#define NOTE_ALIGN(size) (((size) + 3) & ~3)
template <typename HdrType, typename PhdrType, typename NhdrType>
diff --git a/debuggerd/libdebuggerd/include/backtrace.h b/debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
similarity index 100%
rename from debuggerd/libdebuggerd/include/backtrace.h
rename to debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h
diff --git a/debuggerd/libdebuggerd/include/elf_utils.h b/debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
similarity index 100%
rename from debuggerd/libdebuggerd/include/elf_utils.h
rename to debuggerd/libdebuggerd/include/libdebuggerd/elf_utils.h
diff --git a/debuggerd/libdebuggerd/include/machine.h b/debuggerd/libdebuggerd/include/libdebuggerd/machine.h
similarity index 100%
rename from debuggerd/libdebuggerd/include/machine.h
rename to debuggerd/libdebuggerd/include/libdebuggerd/machine.h
diff --git a/debuggerd/libdebuggerd/include/open_files_list.h b/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
similarity index 100%
rename from debuggerd/libdebuggerd/include/open_files_list.h
rename to debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h
diff --git a/debuggerd/libdebuggerd/include/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
similarity index 100%
rename from debuggerd/libdebuggerd/include/tombstone.h
rename to debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
diff --git a/debuggerd/libdebuggerd/include/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
similarity index 100%
rename from debuggerd/libdebuggerd/include/utility.h
rename to debuggerd/libdebuggerd/include/libdebuggerd/utility.h
diff --git a/debuggerd/libdebuggerd/mips/machine.cpp b/debuggerd/libdebuggerd/mips/machine.cpp
index cbf272a..1fc690b 100644
--- a/debuggerd/libdebuggerd/mips/machine.cpp
+++ b/debuggerd/libdebuggerd/mips/machine.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "DEBUG"
+#include "libdebuggerd/machine.h"
+
#include <errno.h>
#include <inttypes.h>
#include <stdint.h>
@@ -25,8 +27,7 @@
#include <backtrace/Backtrace.h>
#include <log/log.h>
-#include "machine.h"
-#include "utility.h"
+#include "libdebuggerd/utility.h"
#define R(x) (static_cast<uintptr_t>(x))
diff --git a/debuggerd/libdebuggerd/mips64/machine.cpp b/debuggerd/libdebuggerd/mips64/machine.cpp
index 0a8d532..955e507 100644
--- a/debuggerd/libdebuggerd/mips64/machine.cpp
+++ b/debuggerd/libdebuggerd/mips64/machine.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "DEBUG"
+#include "libdebuggerd/machine.h"
+
#include <errno.h>
#include <inttypes.h>
#include <stdint.h>
@@ -25,8 +27,7 @@
#include <backtrace/Backtrace.h>
#include <log/log.h>
-#include "machine.h"
-#include "utility.h"
+#include "libdebuggerd/utility.h"
#define R(x) (static_cast<uintptr_t>(x))
diff --git a/debuggerd/libdebuggerd/open_files_list.cpp b/debuggerd/libdebuggerd/open_files_list.cpp
index 5c7ea70..e199db8 100644
--- a/debuggerd/libdebuggerd/open_files_list.cpp
+++ b/debuggerd/libdebuggerd/open_files_list.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "DEBUG"
+#include "libdebuggerd/open_files_list.h"
+
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
@@ -31,9 +33,7 @@
#include <android-base/file.h>
#include <log/log.h>
-#include "open_files_list.h"
-
-#include "utility.h"
+#include "libdebuggerd/utility.h"
void populate_open_files_list(pid_t pid, OpenFilesList* list) {
std::string fd_dir_name = "/proc/" + std::to_string(pid) + "/fd";
diff --git a/debuggerd/libdebuggerd/test/dump_memory_test.cpp b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
index 49f3690..0fad2cf 100644
--- a/debuggerd/libdebuggerd/test/dump_memory_test.cpp
+++ b/debuggerd/libdebuggerd/test/dump_memory_test.cpp
@@ -22,9 +22,10 @@
#include <gtest/gtest.h>
#include <android-base/file.h>
+#include "libdebuggerd/utility.h"
+
#include "BacktraceMock.h"
#include "log_fake.h"
-#include "utility.h"
const char g_expected_full_dump[] =
"\nmemory near r1:\n"
diff --git a/debuggerd/libdebuggerd/test/elf_fake.cpp b/debuggerd/libdebuggerd/test/elf_fake.cpp
index bb52b59..f8cbca7 100644
--- a/debuggerd/libdebuggerd/test/elf_fake.cpp
+++ b/debuggerd/libdebuggerd/test/elf_fake.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "elf_fake.h"
+
#include <stdint.h>
#include <string>
diff --git a/debuggerd/libdebuggerd/test/log_fake.cpp b/debuggerd/libdebuggerd/test/log_fake.cpp
index 3336bcb..68f4013 100644
--- a/debuggerd/libdebuggerd/test/log_fake.cpp
+++ b/debuggerd/libdebuggerd/test/log_fake.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "log_fake.h"
+
#include <errno.h>
#include <stdarg.h>
diff --git a/debuggerd/libdebuggerd/test/open_files_list_test.cpp b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
index 85e0695..acac72c 100644
--- a/debuggerd/libdebuggerd/test/open_files_list_test.cpp
+++ b/debuggerd/libdebuggerd/test/open_files_list_test.cpp
@@ -24,7 +24,7 @@
#include "android-base/test_utils.h"
-#include "open_files_list.h"
+#include "libdebuggerd/open_files_list.h"
// Check that we can produce a list of open files for the current process, and
// that it includes a known open file.
diff --git a/debuggerd/libdebuggerd/test/ptrace_fake.cpp b/debuggerd/libdebuggerd/test/ptrace_fake.cpp
index f40cbd4..0d4080e 100644
--- a/debuggerd/libdebuggerd/test/ptrace_fake.cpp
+++ b/debuggerd/libdebuggerd/test/ptrace_fake.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "ptrace_fake.h"
+
#include <errno.h>
#include <signal.h>
#include <stdarg.h>
@@ -21,8 +23,6 @@
#include <string>
-#include "ptrace_fake.h"
-
siginfo_t g_fake_si = {.si_signo = 0};
void ptrace_set_fake_getsiginfo(const siginfo_t& si) {
diff --git a/debuggerd/libdebuggerd/test/tombstone_test.cpp b/debuggerd/libdebuggerd/test/tombstone_test.cpp
index 6be59e7..934ceba 100644
--- a/debuggerd/libdebuggerd/test/tombstone_test.cpp
+++ b/debuggerd/libdebuggerd/test/tombstone_test.cpp
@@ -22,7 +22,7 @@
#include <gtest/gtest.h>
#include <android-base/file.h>
-#include "utility.h"
+#include "libdebuggerd/utility.h"
#include "BacktraceMock.h"
#include "elf_fake.h"
@@ -113,7 +113,7 @@
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
const char* expected_dump = \
-"\nmemory map:\n"
+"\nmemory map (1 entry):\n"
#if defined(__LP64__)
" 12345678'9abcd000-12345678'9abdefff --- 0 12000\n";
#else
@@ -148,7 +148,7 @@
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
const char* expected_dump = \
-"\nmemory map:\n"
+"\nmemory map (1 entry):\n"
#if defined(__LP64__)
" 12345678'9abcd000-12345678'9abdefff r-- 0 12000 /system/lib/libfake.so (BuildId: abcdef1234567890abcdef1234567890)\n";
#else
@@ -187,7 +187,7 @@
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
const char* expected_dump = \
-"\nmemory map:\n"
+"\nmemory map (2 entries):\n"
#if defined(__LP64__)
" 12345678'9abcd000-12345678'9abdefff -w- 0 12000\n"
" 12345678'9abcd000-12345678'9abdefff -w- 0 12000 /system/lib/libfake.so\n";
@@ -245,7 +245,7 @@
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
const char* expected_dump =
- "\nmemory map:\n"
+ "\nmemory map (5 entries):\n"
#if defined(__LP64__)
" 00000000'0a234000-00000000'0a234fff --- 0 1000\n"
" 00000000'0a334000-00000000'0a334fff r-- f000 1000\n"
@@ -305,7 +305,7 @@
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
const char* expected_dump =
- "\nmemory map: (fault address prefixed with --->)\n"
+ "\nmemory map (3 entries):\n"
#if defined(__LP64__)
"--->Fault address falls at 00000000'00001000 before any mapped regions\n"
" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n"
@@ -363,7 +363,7 @@
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
const char* expected_dump =
- "\nmemory map: (fault address prefixed with --->)\n"
+ "\nmemory map (3 entries): (fault address prefixed with --->)\n"
#if defined(__LP64__)
" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n"
"--->Fault address falls at 00000000'0a533000 between mapped regions\n"
@@ -421,7 +421,7 @@
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
const char* expected_dump =
- "\nmemory map: (fault address prefixed with --->)\n"
+ "\nmemory map (3 entries): (fault address prefixed with --->)\n"
#if defined(__LP64__)
" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n"
"--->00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n"
@@ -481,7 +481,7 @@
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
const char* expected_dump =
- "\nmemory map: (fault address prefixed with --->)\n"
+ "\nmemory map (3 entries): (fault address prefixed with --->)\n"
#if defined(__LP64__)
" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n"
" 00000000'0a534000-00000000'0a534fff --x 3000 1000 (load bias 0x2000)\n"
@@ -521,7 +521,7 @@
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
const char* expected_dump =
- "\nmemory map:\n"
+ "\nmemory map (1 entry):\n"
#if defined(__LP64__)
" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load bias 0xd000)\n";
#else
@@ -571,7 +571,7 @@
}
const char* expected_addr_dump = \
-"\nmemory map: (fault address prefixed with --->)\n"
+"\nmemory map (1 entry):\n"
#if defined(__LP64__)
"--->Fault address falls at 00000000'00001000 before any mapped regions\n"
" 00000000'0a434000-00000000'0a434fff -w- 0 1000\n";
@@ -580,7 +580,7 @@
" 0a434000-0a434fff -w- 0 1000\n";
#endif
const char* expected_dump = \
-"\nmemory map:\n"
+"\nmemory map (1 entry):\n"
#if defined(__LP64__)
" 00000000'0a434000-00000000'0a434fff -w- 0 1000\n";
#else
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index b809ed4..6fb29a9 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "DEBUG"
+#include "libdebuggerd/tombstone.h"
+
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@@ -33,32 +35,30 @@
#include <string>
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <android/log.h>
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
-#include <cutils/properties.h>
#include <log/log.h>
#include <log/logprint.h>
#include <private/android_filesystem_config.h>
+// Needed to get DEBUGGER_SIGNAL.
#include "debuggerd/handler.h"
-#include "backtrace.h"
-#include "elf_utils.h"
-#include "machine.h"
-#include "open_files_list.h"
-#include "tombstone.h"
+#include "libdebuggerd/backtrace.h"
+#include "libdebuggerd/elf_utils.h"
+#include "libdebuggerd/machine.h"
+#include "libdebuggerd/open_files_list.h"
+using android::base::GetBoolProperty;
+using android::base::GetProperty;
using android::base::StringPrintf;
#define STACK_WORDS 16
-#define MAX_TOMBSTONES 10
-#define TOMBSTONE_DIR "/data/tombstones"
-#define TOMBSTONE_TEMPLATE (TOMBSTONE_DIR"/tombstone_%02d")
-
static bool signal_has_si_addr(int si_signo, int si_code) {
// Manually sent signals won't have si_addr.
if (si_code == SI_USER || si_code == SI_QUEUE || si_code == SI_TKILL) {
@@ -208,14 +208,11 @@
}
static void dump_header_info(log_t* log) {
- char fingerprint[PROPERTY_VALUE_MAX];
- char revision[PROPERTY_VALUE_MAX];
+ auto fingerprint = GetProperty("ro.build.fingerprint", "unknown");
+ auto revision = GetProperty("ro.revision", "unknown");
- property_get("ro.build.fingerprint", fingerprint, "unknown");
- property_get("ro.revision", revision, "unknown");
-
- _LOG(log, logtype::HEADER, "Build fingerprint: '%s'\n", fingerprint);
- _LOG(log, logtype::HEADER, "Revision: '%s'\n", revision);
+ _LOG(log, logtype::HEADER, "Build fingerprint: '%s'\n", fingerprint.c_str());
+ _LOG(log, logtype::HEADER, "Revision: '%s'\n", revision.c_str());
_LOG(log, logtype::HEADER, "ABI: '%s'\n", ABI_STRING);
}
@@ -415,16 +412,20 @@
}
ScopedBacktraceMapIteratorLock lock(map);
- _LOG(log, logtype::MAPS, "\n");
- if (!print_fault_address_marker) {
- _LOG(log, logtype::MAPS, "memory map:\n");
- } else {
- _LOG(log, logtype::MAPS, "memory map: (fault address prefixed with --->)\n");
+ _LOG(log, logtype::MAPS,
+ "\n"
+ "memory map (%zu entr%s):",
+ map->size(), map->size() == 1 ? "y" : "ies");
+ if (print_fault_address_marker) {
if (map->begin() != map->end() && addr < map->begin()->start) {
- _LOG(log, logtype::MAPS, "--->Fault address falls at %s before any mapped regions\n",
+ _LOG(log, logtype::MAPS, "\n--->Fault address falls at %s before any mapped regions\n",
get_addr_string(addr).c_str());
print_fault_address_marker = false;
+ } else {
+ _LOG(log, logtype::MAPS, " (fault address prefixed with --->)\n");
}
+ } else {
+ _LOG(log, logtype::MAPS, "\n");
}
std::string line;
@@ -722,9 +723,7 @@
const std::string& process_name, const std::map<pid_t, std::string>& threads,
uintptr_t abort_msg_address) {
// don't copy log messages to tombstone unless this is a dev device
- char value[PROPERTY_VALUE_MAX];
- property_get("ro.debuggable", value, "0");
- bool want_logs = (value[0] == '1');
+ bool want_logs = GetBoolProperty("ro.debuggable", false);
_LOG(log, logtype::HEADER,
"*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n");
@@ -764,59 +763,6 @@
}
}
-// open_tombstone - find an available tombstone slot, if any, of the
-// form tombstone_XX where XX is 00 to MAX_TOMBSTONES-1, inclusive. If no
-// file is available, we reuse the least-recently-modified file.
-int open_tombstone(std::string* out_path) {
- // In a single pass, find an available slot and, in case none
- // exist, find and record the least-recently-modified file.
- char path[128];
- int fd = -1;
- int oldest = -1;
- struct stat oldest_sb;
- for (int i = 0; i < MAX_TOMBSTONES; i++) {
- snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, i);
-
- struct stat sb;
- if (stat(path, &sb) == 0) {
- if (oldest < 0 || sb.st_mtime < oldest_sb.st_mtime) {
- oldest = i;
- oldest_sb.st_mtime = sb.st_mtime;
- }
- continue;
- }
- if (errno != ENOENT) continue;
-
- fd = open(path, O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
- if (fd < 0) continue; // raced ?
-
- if (out_path) {
- *out_path = path;
- }
- fchown(fd, AID_SYSTEM, AID_SYSTEM);
- return fd;
- }
-
- if (oldest < 0) {
- ALOGE("debuggerd: failed to find a valid tombstone, default to using tombstone 0.\n");
- oldest = 0;
- }
-
- // we didn't find an available file, so we clobber the oldest one
- snprintf(path, sizeof(path), TOMBSTONE_TEMPLATE, oldest);
- fd = open(path, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW | O_CLOEXEC, 0600);
- if (fd < 0) {
- ALOGE("debuggerd: failed to open tombstone file '%s': %s\n", path, strerror(errno));
- return -1;
- }
-
- if (out_path) {
- *out_path = path;
- }
- fchown(fd, AID_SYSTEM, AID_SYSTEM);
- return fd;
-}
-
void engrave_tombstone(int tombstone_fd, BacktraceMap* map, BacktraceMap* map_new,
const OpenFilesList* open_files, pid_t pid, pid_t tid,
const std::string& process_name, const std::map<pid_t, std::string>& threads,
@@ -855,10 +801,22 @@
dump_abort_message(backtrace.get(), &log, abort_msg_address);
dump_registers(&log, ucontext);
- // TODO: Dump registers from the ucontext.
if (backtrace->Unwind(0, ucontext)) {
dump_backtrace_and_stack(backtrace.get(), &log);
} else {
ALOGE("Unwind failed: pid = %d, tid = %d", pid, tid);
}
+
+ // TODO: Make this match the format of dump_all_maps above.
+ _LOG(&log, logtype::MAPS, "memory map:\n");
+ android::base::unique_fd maps_fd(open("/proc/self/maps", O_RDONLY | O_CLOEXEC));
+ if (maps_fd == -1) {
+ _LOG(&log, logtype::MAPS, " failed to open /proc/self/maps: %s", strerror(errno));
+ } else {
+ char buf[256];
+ ssize_t rc;
+ while ((rc = TEMP_FAILURE_RETRY(read(maps_fd.get(), buf, sizeof(buf)))) > 0) {
+ android::base::WriteFully(tombstone_fd, buf, rc);
+ }
+ }
}
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 7f450e6..1b74652 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "DEBUG"
-#include "utility.h"
+#include "libdebuggerd/utility.h"
#include <errno.h>
#include <signal.h>
diff --git a/debuggerd/libdebuggerd/x86/machine.cpp b/debuggerd/libdebuggerd/x86/machine.cpp
index af10817..09a64cd 100644
--- a/debuggerd/libdebuggerd/x86/machine.cpp
+++ b/debuggerd/libdebuggerd/x86/machine.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "DEBUG"
+#include "libdebuggerd/machine.h"
+
#include <errno.h>
#include <stdint.h>
#include <string.h>
@@ -24,8 +26,7 @@
#include <backtrace/Backtrace.h>
#include <log/log.h>
-#include "machine.h"
-#include "utility.h"
+#include "libdebuggerd/utility.h"
void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
struct pt_regs r;
diff --git a/debuggerd/libdebuggerd/x86_64/machine.cpp b/debuggerd/libdebuggerd/x86_64/machine.cpp
index bf2c2b4..de1c268 100644
--- a/debuggerd/libdebuggerd/x86_64/machine.cpp
+++ b/debuggerd/libdebuggerd/x86_64/machine.cpp
@@ -16,6 +16,8 @@
#define LOG_TAG "DEBUG"
+#include "libdebuggerd/machine.h"
+
#include <errno.h>
#include <stdint.h>
#include <string.h>
@@ -25,8 +27,7 @@
#include <backtrace/Backtrace.h>
#include <log/log.h>
-#include "machine.h"
-#include "utility.h"
+#include "libdebuggerd/utility.h"
void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
struct user_regs_struct r;
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index 24960bc..c446dbb 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -185,8 +185,8 @@
}
InterceptManager::InterceptManager(event_base* base, int intercept_socket) : base(base) {
- this->listener = evconnlistener_new(base, intercept_accept_cb, this, -1, LEV_OPT_CLOSE_ON_FREE,
- intercept_socket);
+ this->listener = evconnlistener_new(base, intercept_accept_cb, this, LEV_OPT_CLOSE_ON_FREE,
+ /* backlog */ -1, intercept_socket);
}
bool InterceptManager::GetIntercept(pid_t pid, DebuggerdDumpType dump_type,
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 93c7fb5..1bf8f14 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -389,8 +389,9 @@
intercept_manager = new InterceptManager(base, intercept_socket);
- evconnlistener* tombstone_listener = evconnlistener_new(
- base, crash_accept_cb, CrashQueue::for_tombstones(), -1, LEV_OPT_CLOSE_ON_FREE, crash_socket);
+ evconnlistener* tombstone_listener =
+ evconnlistener_new(base, crash_accept_cb, CrashQueue::for_tombstones(), LEV_OPT_CLOSE_ON_FREE,
+ -1 /* backlog */, crash_socket);
if (!tombstone_listener) {
LOG(FATAL) << "failed to create evconnlistener for tombstones.";
}
@@ -402,8 +403,9 @@
}
evutil_make_socket_nonblocking(java_trace_socket);
- evconnlistener* java_trace_listener = evconnlistener_new(
- base, crash_accept_cb, CrashQueue::for_anrs(), -1, LEV_OPT_CLOSE_ON_FREE, java_trace_socket);
+ evconnlistener* java_trace_listener =
+ evconnlistener_new(base, crash_accept_cb, CrashQueue::for_anrs(), LEV_OPT_CLOSE_ON_FREE,
+ -1 /* backlog */, java_trace_socket);
if (!java_trace_listener) {
LOG(FATAL) << "failed to create evconnlistener for java traces.";
}
diff --git a/demangle/Android.bp b/demangle/Android.bp
index e55c886..89b8772 100644
--- a/demangle/Android.bp
+++ b/demangle/Android.bp
@@ -24,6 +24,12 @@
"-Werror",
"-Wextra",
],
+
+ target: {
+ linux_bionic: {
+ enabled: true,
+ },
+ },
}
cc_library {
diff --git a/demangle/DemangleTest.cpp b/demangle/DemangleTest.cpp
index f56a9be..c93e2ab 100644
--- a/demangle/DemangleTest.cpp
+++ b/demangle/DemangleTest.cpp
@@ -428,6 +428,14 @@
ASSERT_EQ("_ZN3one3twoEDa", demangler.Parse("_ZN3one3twoEDa", 12));
}
+TEST(DemangleTest, BooleanLiterals) {
+ Demangler demangler;
+
+ ASSERT_EQ("one<true>", demangler.Parse("_ZN3oneILb1EEE"));
+ ASSERT_EQ("one<false>", demangler.Parse("_ZN3oneILb0EEE"));
+ ASSERT_EQ("one<false, true>", demangler.Parse("_ZN3oneILb0ELb1EEE"));
+}
+
TEST(DemangleTest, demangle) {
std::string str;
diff --git a/demangle/Demangler.cpp b/demangle/Demangler.cpp
index c0a96aa..f148b21 100644
--- a/demangle/Demangler.cpp
+++ b/demangle/Demangler.cpp
@@ -660,6 +660,29 @@
return nullptr;
}
+const char* Demangler::ParseTemplateLiteral(const char* name) {
+ if (*name == 'E') {
+ parse_func_ = parse_funcs_.back();
+ parse_funcs_.pop_back();
+ return name + 1;
+ }
+ // Only understand boolean values with 0 or 1.
+ if (*name == 'b') {
+ name++;
+ if (*name == '0') {
+ AppendArgument("false");
+ cur_state_.str.clear();
+ } else if (*name == '1') {
+ AppendArgument("true");
+ cur_state_.str.clear();
+ } else {
+ return nullptr;
+ }
+ return name + 1;
+ }
+ return nullptr;
+}
+
const char* Demangler::ParseTemplateArgumentsComplex(const char* name) {
if (*name == 'E') {
if (parse_funcs_.empty()) {
@@ -670,6 +693,11 @@
FinalizeTemplate();
Save(cur_state_.str, false);
return name + 1;
+ } else if (*name == 'L') {
+ // Literal value for a template.
+ parse_funcs_.push_back(parse_func_);
+ parse_func_ = &Demangler::ParseTemplateLiteral;
+ return name + 1;
}
return ParseArguments(name);
}
diff --git a/demangle/Demangler.h b/demangle/Demangler.h
index 3bd4f3c..f76def6 100644
--- a/demangle/Demangler.h
+++ b/demangle/Demangler.h
@@ -92,6 +92,7 @@
const char* ParseArguments(const char* name);
const char* ParseTemplateArguments(const char* name);
const char* ParseTemplateArgumentsComplex(const char* name);
+ const char* ParseTemplateLiteral(const char* name);
const char* ParseFunctionArgument(const char* name);
const char* ParseFunctionName(const char* name);
const char* FindFunctionName(const char* name);
diff --git a/fs_mgr/Android.bp b/fs_mgr/Android.bp
index 608917a..5a6298e 100644
--- a/fs_mgr/Android.bp
+++ b/fs_mgr/Android.bp
@@ -37,7 +37,6 @@
"fs_mgr_avb_ops.cpp",
],
static_libs: [
- "liblogwrap",
"libfec",
"libfec_rs",
"libbase",
@@ -53,14 +52,18 @@
"libfstab",
],
whole_static_libs: [
+ "liblogwrap",
"libfstab",
],
+ cppflags: [
+ "-DALLOW_ADBD_DISABLE_VERITY=0",
+ ],
product_variables: {
debuggable: {
- cppflags: ["-DALLOW_ADBD_DISABLE_VERITY=1"],
- },
- eng: {
- cppflags: ["-DALLOW_SKIP_SECURE_CHECK=1"],
+ cppflags: [
+ "-UALLOW_ADBD_DISABLE_VERITY",
+ "-DALLOW_ADBD_DISABLE_VERITY=1",
+ ],
},
},
}
diff --git a/fs_mgr/Android.mk b/fs_mgr/Android.mk
index 18ccc43..007189d 100644
--- a/fs_mgr/Android.mk
+++ b/fs_mgr/Android.mk
@@ -20,6 +20,7 @@
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_MODULE:= fs_mgr
LOCAL_MODULE_TAGS := optional
+LOCAL_REQUIRED_MODULES := mke2fs mke2fs.conf e2fsdroid
LOCAL_FORCE_STATIC_EXECUTABLE := true
LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/sbin
LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 874189a..074f838 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -543,15 +543,6 @@
return ret;
}
-static int device_is_force_encrypted() {
- int ret = -1;
- char value[PROP_VALUE_MAX];
- ret = __system_property_get("ro.vold.forceencryption", value);
- if (ret < 0)
- return 0;
- return strcmp(value, "1") ? 0 : 1;
-}
-
/*
* Tries to mount any of the consecutive fstab entries that match
* the mountpoint of the one given by fstab->recs[start_idx].
@@ -726,7 +717,9 @@
static bool needs_block_encryption(const struct fstab_rec* rec)
{
- if (device_is_force_encrypted() && fs_mgr_is_encryptable(rec)) return true;
+ if (android::base::GetBoolProperty("ro.vold.forceencryption", false) &&
+ fs_mgr_is_encryptable(rec))
+ return true;
if (rec->fs_mgr_flags & MF_FORCECRYPT) return true;
if (rec->fs_mgr_flags & MF_CRYPT) {
/* Check for existence of convert_fde breadcrumb file */
@@ -768,23 +761,6 @@
}
}
-bool is_device_secure() {
- int ret = -1;
- char value[PROP_VALUE_MAX];
- ret = __system_property_get("ro.secure", value);
- if (ret == 0) {
-#ifdef ALLOW_SKIP_SECURE_CHECK
- // Allow eng builds to skip this check if the property
- // is not readable (happens during early mount)
- return false;
-#else
- // If error and not an 'eng' build, we want to fail secure.
- return true;
-#endif
- }
- return strcmp(value, "0") ? true : false;
-}
-
/* When multiple fstab records share the same mount_point, it will
* try to mount each one in turn, and ignore any duplicates after a
* first successful mount.
@@ -857,7 +833,7 @@
/* Skips mounting the device. */
continue;
}
- } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && is_device_secure()) {
+ } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY)) {
int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
if (__android_log_is_debuggable() &&
(rc == FS_MGR_SETUP_VERITY_DISABLED ||
@@ -1064,7 +1040,7 @@
/* Skips mounting the device. */
continue;
}
- } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && is_device_secure()) {
+ } else if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY)) {
int rc = fs_mgr_setup_verity(&fstab->recs[i], true);
if (__android_log_is_debuggable() &&
(rc == FS_MGR_SETUP_VERITY_DISABLED ||
diff --git a/fs_mgr/fs_mgr_format.cpp b/fs_mgr/fs_mgr_format.cpp
index a03d92c..75feee7 100644
--- a/fs_mgr/fs_mgr_format.cpp
+++ b/fs_mgr/fs_mgr_format.cpp
@@ -24,25 +24,21 @@
#include <cutils/partition_utils.h>
#include <sys/mount.h>
-#include <ext4_utils/ext4_utils.h>
#include <ext4_utils/ext4.h>
-#include <ext4_utils/make_ext4fs.h>
-#include <selinux/selinux.h>
-#include <selinux/label.h>
+#include <ext4_utils/ext4_utils.h>
+#include <logwrap/logwrap.h>
#include <selinux/android.h>
+#include <selinux/label.h>
+#include <selinux/selinux.h>
#include "fs_mgr_priv.h"
#include "cryptfs.h"
-extern "C" {
-extern struct fs_info info; /* magic global from ext4_utils */
-extern void reset_ext4fs_info();
-}
-
static int format_ext4(char *fs_blkdev, char *fs_mnt_point, bool crypt_footer)
{
uint64_t dev_sz;
int fd, rc = 0;
+ int status;
if ((fd = open(fs_blkdev, O_WRONLY)) < 0) {
PERROR << "Cannot open block device";
@@ -55,30 +51,36 @@
return -1;
}
- struct selabel_handle *sehandle = selinux_android_file_context_handle();
- if (!sehandle) {
- /* libselinux logs specific error */
- LERROR << "Cannot initialize android file_contexts";
- close(fd);
- return -1;
- }
-
- /* Format the partition using the calculated length */
- reset_ext4fs_info();
- info.len = (off64_t)dev_sz;
- if (crypt_footer) {
- info.len -= CRYPT_FOOTER_OFFSET;
- }
-
- /* Use make_ext4fs_internal to avoid wiping an already-wiped partition. */
- rc = make_ext4fs_internal(fd, NULL, NULL, fs_mnt_point, 0, 0, 0, 0, 0, 0, sehandle, 0, 0, NULL, NULL, NULL);
- if (rc) {
- LERROR << "make_ext4fs returned " << rc;
- }
close(fd);
- if (sehandle) {
- selabel_close(sehandle);
+ /* Format the partition using the calculated length */
+ if (crypt_footer) {
+ dev_sz -= CRYPT_FOOTER_OFFSET;
+ }
+
+ std::string size_str = std::to_string(dev_sz / 4096);
+ const char* const mke2fs_args[] = {
+ "/system/bin/mke2fs", "-t", "ext4", "-b", "4096", fs_blkdev, size_str.c_str(), nullptr};
+
+ rc = android_fork_execvp_ext(arraysize(mke2fs_args), const_cast<char**>(mke2fs_args), &status,
+ true, LOG_KLOG, true, nullptr, nullptr, 0);
+ if (rc) {
+ LERROR << "mke2fs returned " << rc;
+ return rc;
+ }
+
+ const char* const e2fsdroid_args[] = {
+ "/system/bin/e2fsdroid",
+ "-e",
+ "-a",
+ fs_mnt_point,
+ fs_blkdev,
+ nullptr};
+
+ rc = android_fork_execvp_ext(arraysize(e2fsdroid_args), const_cast<char**>(e2fsdroid_args),
+ &status, true, LOG_KLOG, true, nullptr, nullptr, 0);
+ if (rc) {
+ LERROR << "e2fsdroid returned " << rc;
}
return rc;
@@ -86,44 +88,11 @@
static int format_f2fs(char *fs_blkdev)
{
- char * args[5];
- int pid;
- int rc = 0;
+ int status;
+ const char* const args[] = {"/system/bin/make_f2fs", "-f", "-O encrypt", fs_blkdev, nullptr};
- args[0] = (char *)"/system/bin/make_f2fs";
- args[1] = (char *)"-f";
- args[2] = (char *)"-O encrypt";
- args[3] = fs_blkdev;
- args[4] = (char *)0;
-
- pid = fork();
- if (pid < 0) {
- return pid;
- }
- if (!pid) {
- /* This doesn't return */
- execv(args[0], args);
- exit(1);
- }
- for(;;) {
- pid_t p = waitpid(pid, &rc, 0);
- if (p != pid) {
- LERROR << "Error waiting for child process - " << p;
- rc = -1;
- break;
- }
- if (WIFEXITED(rc)) {
- rc = WEXITSTATUS(rc);
- LINFO << args[0] << " done, status " << rc;
- if (rc) {
- rc = -1;
- }
- break;
- }
- LERROR << "Still waiting for " << args[0] << "...";
- }
-
- return rc;
+ return android_fork_execvp_ext(arraysize(args), const_cast<char**>(args), &status, true,
+ LOG_KLOG, true, nullptr, nullptr, 0);
}
int fs_mgr_do_format(struct fstab_rec *fstab, bool crypt_footer)
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 41a5868..bce245c 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -779,44 +779,19 @@
}
/*
- * 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 char *path)
-{
- int i;
+struct fstab_rec* fs_mgr_get_entry_for_mount_point(struct fstab* fstab, const std::string& path) {
if (!fstab) {
- return NULL;
+ 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++) {
- int len = strlen(fstab->recs[i].mount_point);
- if (strncmp(path, fstab->recs[i].mount_point, len) == 0 &&
- (path[len] == '\0' || path[len] == '/')) {
+ 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 NULL;
-}
-
-/*
- * 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);
+ return nullptr;
}
int fs_mgr_is_voldmanaged(const struct fstab_rec *fstab)
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 7423c1f..c3c87fa 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -121,7 +121,6 @@
bool fs_mgr_is_device_unlocked();
const std::string& get_android_dt_dir();
bool is_dt_compatible();
-bool is_device_secure();
int load_verity_state(struct fstab_rec* fstab, int* mode);
#endif /* __CORE_FS_MGR_PRIV_H */
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index 9ca15e2..33fd562 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -21,19 +21,12 @@
#include "fs_mgr.h"
#include "fs_mgr_priv.h"
-// Returns "_a" or "_b" based on two possible values in kernel cmdline:
-// - androidboot.slot = a or b OR
-// - androidboot.slot_suffix = _a or _b
-// TODO: remove slot_suffix once it's deprecated.
+// Returns "_a" or "_b" based on androidboot.slot_suffix in kernel cmdline, or an empty string
+// if that parameter does not exist.
std::string fs_mgr_get_slot_suffix() {
- std::string slot;
std::string ab_suffix;
- if (fs_mgr_get_boot_config("slot", &slot)) {
- ab_suffix = "_" + slot;
- } else if (!fs_mgr_get_boot_config("slot_suffix", &ab_suffix)) {
- ab_suffix = "";
- }
+ fs_mgr_get_boot_config("slot_suffix", &ab_suffix);
return ab_suffix;
}
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 7f8e1e2..896b603 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -765,13 +765,6 @@
const std::string mount_point(basename(fstab->mount_point));
bool verified_at_boot = false;
- // This is a public API and so deserves its own check to see if verity
- // setup is needed at all.
- if (!is_device_secure()) {
- LINFO << "Verity setup skipped for " << mount_point;
- return FS_MGR_SETUP_VERITY_SKIPPED;
- }
-
if (fec_open(&f, fstab->blk_device, O_RDONLY, FEC_VERITY_DISABLE,
FEC_DEFAULT_ROOTS) < 0) {
PERROR << "Failed to open '" << fstab->blk_device << "'";
@@ -792,7 +785,7 @@
#ifdef ALLOW_ADBD_DISABLE_VERITY
if (verity.disabled) {
retval = FS_MGR_SETUP_VERITY_DISABLED;
- LINFO << "Attempt to cleanly disable verity - only works in USERDEBUG";
+ LINFO << "Attempt to cleanly disable verity - only works in USERDEBUG/ENG";
goto out;
}
#endif
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 3d3faf3..5c26c2e 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -31,8 +31,6 @@
// turn verity off in userdebug builds.
#define VERITY_METADATA_MAGIC_DISABLE 0x46464f56 // "VOFF"
-__BEGIN_DECLS
-
// Verity modes
enum verity_mode {
VERITY_MODE_EIO = 0,
@@ -85,6 +83,4 @@
#define FS_MGR_SETUP_VERITY_SUCCESS 0
int fs_mgr_setup_verity(struct fstab_rec *fstab, bool wait_for_verity_dev);
-__END_DECLS
-
#endif /* __CORE_FS_MGR_H */
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 8a18ec0..bc2942d 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -22,13 +22,7 @@
#include <stdint.h>
#include <stdio.h>
-// C++ only headers
-// TODO: move this into separate header files under include/fs_mgr/*.h
-#ifdef __cplusplus
#include <string>
-#endif
-
-__BEGIN_DECLS
/*
* The entries must be kept in the same order as they were seen in the fstab.
@@ -70,7 +64,7 @@
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);
+struct fstab_rec* fs_mgr_get_entry_for_mount_point(struct fstab* fstab, const std::string& 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);
@@ -89,12 +83,6 @@
int fs_mgr_is_latemount(const struct fstab_rec* fstab);
int fs_mgr_is_quota(const struct fstab_rec* fstab);
-__END_DECLS
-
-// C++ only functions
-// TODO: move this into separate header files under include/fs_mgr/*.h
-#ifdef __cplusplus
std::string fs_mgr_get_slot_suffix();
-#endif
#endif /* __CORE_FS_TAB_H */
diff --git a/gatekeeperd/SoftGateKeeper.h b/gatekeeperd/SoftGateKeeper.h
index 92d1752..2f4f4d7 100644
--- a/gatekeeperd/SoftGateKeeper.h
+++ b/gatekeeperd/SoftGateKeeper.h
@@ -27,10 +27,10 @@
#include <android-base/memory.h>
#include <gatekeeper/gatekeeper.h>
-#include <nativehelper/UniquePtr.h>
#include <iostream>
#include <unordered_map>
+#include <memory>
namespace gatekeeper {
@@ -173,7 +173,7 @@
typedef std::unordered_map<uint32_t, failure_record_t> FailureRecordMap;
typedef std::unordered_map<uint64_t, fast_hash_t> FastHashMap;
- UniquePtr<uint8_t[]> key_;
+ std::unique_ptr<uint8_t[]> key_;
FailureRecordMap failure_map_;
FastHashMap fast_hash_map_;
};
diff --git a/gatekeeperd/SoftGateKeeperDevice.h b/gatekeeperd/SoftGateKeeperDevice.h
index 229f9a9..e3dc068 100644
--- a/gatekeeperd/SoftGateKeeperDevice.h
+++ b/gatekeeperd/SoftGateKeeperDevice.h
@@ -19,7 +19,7 @@
#include "SoftGateKeeper.h"
-#include <nativehelper/UniquePtr.h>
+#include <memory>
using namespace gatekeeper;
@@ -68,7 +68,7 @@
const uint8_t *provided_password, uint32_t provided_password_length,
uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll);
private:
- UniquePtr<SoftGateKeeper> impl_;
+ std::unique_ptr<SoftGateKeeper> impl_;
};
} // namespace gatekeeper
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index e6eb3bc..73dab9b 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -23,6 +23,7 @@
#include <inttypes.h>
#include <stdint.h>
#include <unistd.h>
+#include <memory>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -375,7 +376,7 @@
private:
sp<IGatekeeper> hw_device;
- UniquePtr<SoftGateKeeperDevice> soft_device;
+ std::unique_ptr<SoftGateKeeperDevice> soft_device;
};
}// namespace android
diff --git a/gatekeeperd/tests/gatekeeper_test.cpp b/gatekeeperd/tests/gatekeeper_test.cpp
index b3aea7b..100375f 100644
--- a/gatekeeperd/tests/gatekeeper_test.cpp
+++ b/gatekeeperd/tests/gatekeeper_test.cpp
@@ -19,7 +19,6 @@
#include <gtest/gtest.h>
#include <hardware/hw_auth_token.h>
-#include <nativehelper/UniquePtr.h>
#include "../SoftGateKeeper.h"
diff --git a/init/Android.bp b/init/Android.bp
index 33dfe56..e906771 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -72,10 +72,14 @@
"import_parser.cpp",
"log.cpp",
"parser.cpp",
+ "persistent_properties.cpp",
+ "persistent_properties.proto",
"property_service.cpp",
"security.cpp",
"selinux.cpp",
"service.cpp",
+ "subcontext.cpp",
+ "subcontext.proto",
"rlimit_parser.cpp",
"tokenizer.cpp",
"uevent_listener.cpp",
@@ -89,11 +93,15 @@
"liblog",
"libprocessgroup",
"libfs_mgr",
+ "libprotobuf-cpp-lite",
],
include_dirs: [
"system/core/mkbootimg",
],
-
+ proto: {
+ type: "lite",
+ export_proto_headers: true,
+ },
}
/*
@@ -118,7 +126,7 @@
"init_first_stage.cpp",
"keychords.cpp",
"reboot.cpp",
- "signal_handler.cpp",
+ "sigchld_handler.cpp",
"ueventd.cpp",
"watchdogd.cpp",
],
@@ -162,10 +170,12 @@
srcs: [
"devices_test.cpp",
"init_test.cpp",
+ "persistent_properties_test.cpp",
"property_service_test.cpp",
"result_test.cpp",
"rlimit_parser_test.cpp",
"service_test.cpp",
+ "subcontext_test.cpp",
"ueventd_test.cpp",
"util_test.cpp",
],
@@ -177,6 +187,25 @@
"libinit",
"libselinux",
"libcrypto",
+ "libprotobuf-cpp-lite",
+ ],
+}
+
+cc_benchmark {
+ name: "init_benchmarks",
+ defaults: ["init_defaults"],
+ srcs: [
+ "subcontext_benchmark.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ ],
+ static_libs: [
+ "libinit",
+ "libselinux",
+ "libcrypto",
+ "libprotobuf-cpp-lite",
],
}
diff --git a/init/Android.mk b/init/Android.mk
index 23ada73..dd0f1bf 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -48,7 +48,7 @@
init_first_stage.cpp \
keychords.cpp \
reboot.cpp \
- signal_handler.cpp \
+ sigchld_handler.cpp \
ueventd.cpp \
watchdogd.cpp \
@@ -82,6 +82,7 @@
libprocessgroup \
libavb \
libkeyutils \
+ libprotobuf-cpp-lite \
LOCAL_REQUIRED_MODULES := \
e2fsdroid \
diff --git a/init/README.md b/init/README.md
index b681f21..d7edf21 100644
--- a/init/README.md
+++ b/init/README.md
@@ -381,6 +381,11 @@
within _argument_.
Init halts executing commands until the forked process exits.
+`exec_background [ <seclabel> [ <user> [ <group>\* ] ] ] -- <command> [ <argument>\* ]`
+> Fork and execute command with the given arguments. This is handled similarly
+ to the `exec` command. The difference is that init does not halt executing
+ commands until the process exits for `exec_background`.
+
`exec_start <service>`
> Start a given service and halt the processing of additional init commands
until it returns. The command functions similarly to the `exec` command,
@@ -453,8 +458,9 @@
`rmdir <path>`
> Calls rmdir(2) on the given path.
-`readahead <file|dir>`
+`readahead <file|dir> [--fully]`
> Calls readahead(2) on the file or files within given directory.
+ Use option --fully to read the full file content.
`setprop <name> <value>`
> Set system property _name_ to _value_. Properties are expanded
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 e2e3d93..027b392 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -56,6 +56,7 @@
#include <selinux/android.h>
#include <selinux/label.h>
#include <selinux/selinux.h>
+#include <system/thread_defs.h>
#include "action.h"
#include "bootchart.h"
@@ -65,7 +66,7 @@
#include "reboot.h"
#include "rlimit_parser.h"
#include "service.h"
-#include "signal_handler.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,7 +156,20 @@
return Success();
}
-static Result<Success> do_exec_start(const std::vector<std::string>& 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";
+ }
+ if (auto result = service->Start(); !result) {
+ return Error() << "Could not start exec background service: " << result.error();
+ }
+
+ ServiceList::GetInstance().AddService(std::move(service));
+ return Success();
+}
+
+static Result<Success> do_exec_start(const BuiltinArguments& args) {
Service* service = ServiceList::GetInstance().FindService(args[1]);
if (!service) {
return Error() << "Service not found";
@@ -161,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);
@@ -196,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;
@@ -218,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);
@@ -267,14 +288,14 @@
"--prompt_and_wipe_data",
"--reason=set_policy_failed:"s + args[1]};
reboot_into_recovery(options);
- return Error() << "reboot into recovery failed";
+ return Success();
}
}
return Success();
}
/* 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";
}
@@ -306,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;
@@ -472,7 +493,7 @@
PLOG(ERROR) << "fs_mgr_mount_all suggested recovery, so wiping data via recovery.";
const std::vector<std::string> options = {"--wipe_data", "--reason=fs_mgr_mount_all" };
reboot_into_recovery(options);
- return Error() << "reboot_into_recovery() failed";
+ return Success();
/* If reboot worked, there is no return. */
} else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
if (e4crypt_install_keyring()) {
@@ -498,7 +519,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;
@@ -531,7 +552,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) {
@@ -546,7 +567,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;
@@ -558,13 +579,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) {
@@ -573,7 +594,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) {
@@ -582,26 +603,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.
@@ -613,21 +634,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";
@@ -639,7 +660,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) {
@@ -655,14 +676,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();
}
@@ -670,13 +691,40 @@
return Success();
}
-static Result<Success> do_readahead(const std::vector<std::string>& args) {
+static Result<Success> readahead_file(const std::string& filename, bool fully) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY)));
+ if (fd == -1) {
+ return ErrnoError() << "Error opening file";
+ }
+ if (posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED)) {
+ return ErrnoError() << "Error posix_fadvise file";
+ }
+ if (readahead(fd, 0, std::numeric_limits<size_t>::max())) {
+ return ErrnoError() << "Error readahead file";
+ }
+ if (fully) {
+ char buf[BUFSIZ];
+ ssize_t n;
+ while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) {
+ }
+ if (n != 0) {
+ return ErrnoError() << "Error reading file";
+ }
+ }
+ return Success();
+}
+
+static Result<Success> do_readahead(const BuiltinArguments& args) {
struct stat sb;
if (stat(args[1].c_str(), &sb)) {
return ErrnoError() << "Error opening " << args[1];
}
+ bool readfully = false;
+ if (args.size() == 3 && args[2] == "--fully") {
+ readfully = true;
+ }
// We will do readahead in a forked process in order not to block init
// since it may block while it reads the
// filesystem metadata needed to locate the requested blocks. This
@@ -685,15 +733,16 @@
// the requested data has been read.
pid_t pid = fork();
if (pid == 0) {
+ if (setpriority(PRIO_PROCESS, 0, static_cast<int>(ANDROID_PRIORITY_LOWEST)) != 0) {
+ PLOG(WARNING) << "setpriority failed";
+ }
+ if (android_set_ioprio(0, IoSchedClass_IDLE, 7)) {
+ PLOG(WARNING) << "ioprio_get failed";
+ }
android::base::Timer t;
if (S_ISREG(sb.st_mode)) {
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(args[1].c_str(), O_RDONLY)));
- if (fd == -1) {
- PLOG(ERROR) << "Error opening file: " << args[1];
- _exit(EXIT_FAILURE);
- }
- if (readahead(fd, 0, std::numeric_limits<size_t>::max())) {
- PLOG(ERROR) << "Error readahead file: " << args[1];
+ if (auto result = readahead_file(args[1], readfully); !result) {
+ LOG(WARNING) << "Unable to readahead '" << args[1] << "': " << result.error();
_exit(EXIT_FAILURE);
}
} else if (S_ISDIR(sb.st_mode)) {
@@ -708,19 +757,15 @@
for (FTSENT* ftsent = fts_read(fts.get()); ftsent != nullptr;
ftsent = fts_read(fts.get())) {
if (ftsent->fts_info & FTS_F) {
- android::base::unique_fd fd(
- TEMP_FAILURE_RETRY(open(ftsent->fts_accpath, O_RDONLY)));
- if (fd == -1) {
- PLOG(ERROR) << "Error opening file: " << args[1];
- continue;
- }
- if (readahead(fd, 0, std::numeric_limits<size_t>::max())) {
- PLOG(ERROR) << "Unable to readahead on file: " << ftsent->fts_accpath;
+ const std::string filename = ftsent->fts_accpath;
+ if (auto result = readahead_file(filename, readfully); !result) {
+ LOG(WARNING)
+ << "Unable to readahead '" << filename << "': " << result.error();
}
}
}
}
- LOG(INFO) << "Readahead " << args[1] << " took " << t;
+ LOG(INFO) << "Readahead " << args[1] << " took " << t << " asynchronously";
_exit(0);
} else if (pid < 0) {
return ErrnoError() << "Fork failed";
@@ -728,7 +773,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();
@@ -740,7 +785,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();
@@ -777,7 +822,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";
@@ -785,7 +830,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;};
@@ -827,13 +872,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);
@@ -854,17 +899,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;
@@ -881,7 +926,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);
@@ -902,7 +947,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;
@@ -911,63 +956,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_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, 1, 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 678f49f..51a98a2 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -25,6 +25,7 @@
#include <string.h>
#include <sys/epoll.h>
#include <sys/mount.h>
+#include <sys/signalfd.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <unistd.h>
@@ -51,7 +52,7 @@
#include "reboot.h"
#include "security.h"
#include "selinux.h"
-#include "signal_handler.h"
+#include "sigchld_handler.h"
#include "ueventd.h"
#include "util.h"
#include "watchdogd.h"
@@ -72,14 +73,19 @@
std::string default_console = "/dev/console";
static int epoll_fd = -1;
+static int sigterm_signal_fd = -1;
static std::unique_ptr<Timer> waiting_for_prop(nullptr);
static std::string wait_prop_name;
static std::string wait_prop_value;
static bool shutting_down;
+static std::string shutdown_command;
+static bool do_shutdown = false;
std::vector<std::string> late_import_paths;
+static std::vector<Subcontext>* subcontexts;
+
void DumpState() {
ServiceList::GetInstance().DumpState();
ActionManager::GetInstance().DumpState();
@@ -88,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;
@@ -155,9 +161,16 @@
// In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
// commands to be executed.
if (name == "sys.powerctl") {
- if (HandlePowerctlMessage(value)) {
- shutting_down = true;
- }
+ // Despite the above comment, we can't call HandlePowerctlMessage() in this function,
+ // because it modifies the contents of the action queue, which can cause the action queue
+ // to get into a bad state if this function is called from a command being executed by the
+ // action queue. Instead we set this flag and ensure that shutdown happens before the next
+ // command is run in the main init loop.
+ // TODO: once property service is removed from init, this will never happen from a builtin,
+ // but rather from a callback from the property service socket, in which case this hack can
+ // go away.
+ shutdown_command = value;
+ do_shutdown = true;
}
if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
@@ -209,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 "...";
@@ -230,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;
@@ -322,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();
@@ -392,6 +405,40 @@
sigaction(SIGTRAP, &action, nullptr);
}
+static void HandleSigtermSignal() {
+ signalfd_siginfo siginfo;
+ ssize_t bytes_read = TEMP_FAILURE_RETRY(read(sigterm_signal_fd, &siginfo, sizeof(siginfo)));
+ if (bytes_read != sizeof(siginfo)) {
+ PLOG(ERROR) << "Failed to read siginfo from sigterm_signal_fd";
+ return;
+ }
+
+ if (siginfo.ssi_pid != 0) {
+ // Drop any userspace SIGTERM requests.
+ LOG(DEBUG) << "Ignoring SIGTERM from pid " << siginfo.ssi_pid;
+ return;
+ }
+
+ HandlePowerctlMessage("shutdown,container");
+}
+
+static void InstallSigtermHandler() {
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGTERM);
+
+ if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {
+ PLOG(FATAL) << "failed to block SIGTERM";
+ }
+
+ sigterm_signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);
+ if (sigterm_signal_fd == -1) {
+ PLOG(FATAL) << "failed to create signalfd for SIGTERM";
+ }
+
+ register_epoll_handler(sigterm_signal_fd, HandleSigtermSignal);
+}
+
int main(int argc, char** argv) {
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
@@ -401,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();
}
@@ -523,11 +576,16 @@
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
- PLOG(ERROR) << "epoll_create1 failed";
- exit(1);
+ PLOG(FATAL) << "epoll_create1 failed";
}
- signal_handler_init();
+ sigchld_handler_init();
+
+ if (!IsRebootCapable()) {
+ // If init does not have the CAP_SYS_BOOT capability, it is running in a container.
+ // In that case, receiving SIGTERM will cause the system to shut down.
+ InstallSigtermHandler();
+ }
property_load_boot_defaults();
export_oem_lock_status();
@@ -537,6 +595,8 @@
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
+ subcontexts = InitializeSubcontexts();
+
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
@@ -579,6 +639,13 @@
// By default, sleep until something happens.
int epoll_timeout_ms = -1;
+ if (do_shutdown && !shutting_down) {
+ do_shutdown = false;
+ if (HandlePowerctlMessage(shutdown_command)) {
+ shutting_down = true;
+ }
+ }
+
if (!(waiting_for_prop || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
}
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/persistent_properties.cpp b/init/persistent_properties.cpp
new file mode 100644
index 0000000..21adce9
--- /dev/null
+++ b/init/persistent_properties.cpp
@@ -0,0 +1,246 @@
+/*
+ * 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 "persistent_properties.h"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/system_properties.h>
+#include <sys/types.h>
+
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include "util.h"
+
+using android::base::ReadFdToString;
+using android::base::StartsWith;
+using android::base::WriteStringToFd;
+using android::base::unique_fd;
+
+namespace android {
+namespace init {
+
+std::string persistent_property_filename = "/data/property/persistent_properties";
+
+namespace {
+
+constexpr const char kLegacyPersistentPropertyDir[] = "/data/property";
+
+void AddPersistentProperty(const std::string& name, const std::string& value,
+ PersistentProperties* persistent_properties) {
+ auto persistent_property_record = persistent_properties->add_properties();
+ persistent_property_record->set_name(name);
+ persistent_property_record->set_value(value);
+}
+
+Result<PersistentProperties> LoadLegacyPersistentProperties() {
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
+ if (!dir) {
+ return ErrnoError() << "Unable to open persistent property directory \""
+ << kLegacyPersistentPropertyDir << "\"";
+ }
+
+ PersistentProperties persistent_properties;
+ dirent* entry;
+ while ((entry = readdir(dir.get())) != nullptr) {
+ if (!StartsWith(entry->d_name, "persist.")) {
+ continue;
+ }
+ if (entry->d_type != DT_REG) {
+ continue;
+ }
+
+ unique_fd fd(openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW));
+ if (fd == -1) {
+ PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
+ continue;
+ }
+
+ struct stat sb;
+ if (fstat(fd, &sb) == -1) {
+ PLOG(ERROR) << "fstat on property file \"" << entry->d_name << "\" failed";
+ continue;
+ }
+
+ // File must not be accessible to others, be owned by root/root, and
+ // not be a hard link to any other file.
+ if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || sb.st_uid != 0 || sb.st_gid != 0 ||
+ sb.st_nlink != 1) {
+ PLOG(ERROR) << "skipping insecure property file " << entry->d_name
+ << " (uid=" << sb.st_uid << " gid=" << sb.st_gid << " nlink=" << sb.st_nlink
+ << " mode=" << std::oct << sb.st_mode << ")";
+ continue;
+ }
+
+ std::string value;
+ if (ReadFdToString(fd, &value)) {
+ AddPersistentProperty(entry->d_name, value, &persistent_properties);
+ } else {
+ PLOG(ERROR) << "Unable to read persistent property file " << entry->d_name;
+ }
+ }
+ return persistent_properties;
+}
+
+void RemoveLegacyPersistentPropertyFiles() {
+ std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);
+ if (!dir) {
+ PLOG(ERROR) << "Unable to open persistent property directory \""
+ << kLegacyPersistentPropertyDir << "\"";
+ return;
+ }
+
+ dirent* entry;
+ while ((entry = readdir(dir.get())) != nullptr) {
+ if (!StartsWith(entry->d_name, "persist.")) {
+ continue;
+ }
+ if (entry->d_type != DT_REG) {
+ continue;
+ }
+ unlinkat(dirfd(dir.get()), entry->d_name, 0);
+ }
+}
+
+PersistentProperties LoadPersistentPropertiesFromMemory() {
+ PersistentProperties persistent_properties;
+ __system_property_foreach(
+ [](const prop_info* pi, void* cookie) {
+ __system_property_read_callback(
+ pi,
+ [](void* cookie, const char* name, const char* value, unsigned serial) {
+ if (StartsWith(name, "persist.")) {
+ auto properties = reinterpret_cast<PersistentProperties*>(cookie);
+ AddPersistentProperty(name, value, properties);
+ }
+ },
+ cookie);
+ },
+ &persistent_properties);
+ return persistent_properties;
+}
+
+Result<std::string> ReadPersistentPropertyFile() {
+ const std::string temp_filename = persistent_property_filename + ".tmp";
+ if (access(temp_filename.c_str(), F_OK) == 0) {
+ LOG(INFO)
+ << "Found temporary property file while attempting to persistent system properties"
+ " a previous persistent property write may have failed";
+ unlink(temp_filename.c_str());
+ }
+ auto file_contents = ReadFile(persistent_property_filename);
+ if (!file_contents) {
+ return Error() << "Unable to read persistent property file: " << file_contents.error();
+ }
+ return *file_contents;
+}
+
+} // namespace
+
+Result<PersistentProperties> LoadPersistentPropertyFile() {
+ auto file_contents = ReadPersistentPropertyFile();
+ if (!file_contents) return file_contents.error();
+
+ PersistentProperties persistent_properties;
+ if (persistent_properties.ParseFromString(*file_contents)) return persistent_properties;
+
+ // If the file cannot be parsed in either format, then we don't have any recovery
+ // mechanisms, so we delete it to allow for future writes to take place successfully.
+ unlink(persistent_property_filename.c_str());
+ return Error() << "Unable to parse persistent property file: Could not parse protobuf";
+}
+
+Result<Success> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {
+ const std::string temp_filename = persistent_property_filename + ".tmp";
+ unique_fd fd(TEMP_FAILURE_RETRY(
+ open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
+ if (fd == -1) {
+ return ErrnoError() << "Could not open temporary properties file";
+ }
+ std::string serialized_string;
+ if (!persistent_properties.SerializeToString(&serialized_string)) {
+ return Error() << "Unable to serialize properties";
+ }
+ if (!WriteStringToFd(serialized_string, fd)) {
+ return ErrnoError() << "Unable to write file contents";
+ }
+ fsync(fd);
+ fd.reset();
+
+ if (rename(temp_filename.c_str(), persistent_property_filename.c_str())) {
+ int saved_errno = errno;
+ unlink(temp_filename.c_str());
+ return Error(saved_errno) << "Unable to rename persistent property file";
+ }
+ return Success();
+}
+
+// Persistent properties are not written often, so we rather not keep any data in memory and read
+// then rewrite the persistent property file for each update.
+void WritePersistentProperty(const std::string& name, const std::string& value) {
+ auto persistent_properties = LoadPersistentPropertyFile();
+
+ if (!persistent_properties) {
+ LOG(ERROR) << "Recovering persistent properties from memory: "
+ << persistent_properties.error();
+ persistent_properties = LoadPersistentPropertiesFromMemory();
+ }
+ auto it = std::find_if(persistent_properties->mutable_properties()->begin(),
+ persistent_properties->mutable_properties()->end(),
+ [&name](const auto& record) { return record.name() == name; });
+ if (it != persistent_properties->mutable_properties()->end()) {
+ it->set_name(name);
+ it->set_value(value);
+ } else {
+ AddPersistentProperty(name, value, &persistent_properties.value());
+ }
+
+ if (auto result = WritePersistentPropertyFile(*persistent_properties); !result) {
+ LOG(ERROR) << "Could not store persistent property: " << result.error();
+ }
+}
+
+PersistentProperties LoadPersistentProperties() {
+ auto persistent_properties = LoadPersistentPropertyFile();
+
+ if (!persistent_properties) {
+ LOG(ERROR) << "Could not load single persistent property file, trying legacy directory: "
+ << persistent_properties.error();
+ persistent_properties = LoadLegacyPersistentProperties();
+ if (!persistent_properties) {
+ LOG(ERROR) << "Unable to load legacy persistent properties: "
+ << persistent_properties.error();
+ return {};
+ }
+ if (auto result = WritePersistentPropertyFile(*persistent_properties); result) {
+ RemoveLegacyPersistentPropertyFiles();
+ } else {
+ LOG(ERROR) << "Unable to write single persistent property file: " << result.error();
+ // Fall through so that we still set the properties that we've read.
+ }
+ }
+
+ return *persistent_properties;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/persistent_properties.h b/init/persistent_properties.h
new file mode 100644
index 0000000..5f4df85
--- /dev/null
+++ b/init/persistent_properties.h
@@ -0,0 +1,39 @@
+/*
+ * 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_PERSISTENT_PROPERTIES_H
+#define _INIT_PERSISTENT_PROPERTIES_H
+
+#include <string>
+
+#include "result.h"
+#include "system/core/init/persistent_properties.pb.h"
+
+namespace android {
+namespace init {
+
+PersistentProperties LoadPersistentProperties();
+void WritePersistentProperty(const std::string& name, const std::string& value);
+
+// Exposed only for testing
+Result<PersistentProperties> LoadPersistentPropertyFile();
+Result<Success> WritePersistentPropertyFile(const PersistentProperties& persistent_properties);
+extern std::string persistent_property_filename;
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/signal_handler.h b/init/persistent_properties.proto
similarity index 64%
copy from init/signal_handler.h
copy to init/persistent_properties.proto
index 9362be5..c8d2e3a 100644
--- a/init/signal_handler.h
+++ b/init/persistent_properties.proto
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * 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.
@@ -14,17 +14,14 @@
* limitations under the License.
*/
-#ifndef _INIT_SIGNAL_HANDLER_H_
-#define _INIT_SIGNAL_HANDLER_H_
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
-namespace android {
-namespace init {
+message PersistentProperties {
+ message PersistentPropertyRecord {
+ optional string name = 1;
+ optional string value = 2;
+ }
-void ReapAnyOutstandingChildren();
-
-void signal_handler_init(void);
-
-} // namespace init
-} // namespace android
-
-#endif
+ repeated PersistentPropertyRecord properties = 1;
+}
diff --git a/init/persistent_properties_test.cpp b/init/persistent_properties_test.cpp
new file mode 100644
index 0000000..872e9a1
--- /dev/null
+++ b/init/persistent_properties_test.cpp
@@ -0,0 +1,156 @@
+/*
+ * 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 "persistent_properties.h"
+
+#include <errno.h>
+
+#include <vector>
+
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "util.h"
+
+using namespace std::string_literals;
+
+namespace android {
+namespace init {
+
+PersistentProperties VectorToPersistentProperties(
+ const std::vector<std::pair<std::string, std::string>>& input_properties) {
+ PersistentProperties persistent_properties;
+
+ for (const auto& [name, value] : input_properties) {
+ auto persistent_property_record = persistent_properties.add_properties();
+ persistent_property_record->set_name(name);
+ persistent_property_record->set_value(value);
+ }
+
+ return persistent_properties;
+}
+
+void CheckPropertiesEqual(std::vector<std::pair<std::string, std::string>> expected,
+ const PersistentProperties& persistent_properties) {
+ for (const auto& persistent_property_record : persistent_properties.properties()) {
+ auto it = std::find_if(expected.begin(), expected.end(),
+ [persistent_property_record](const auto& entry) {
+ return entry.first == persistent_property_record.name() &&
+ entry.second == persistent_property_record.value();
+ });
+ ASSERT_TRUE(it != expected.end())
+ << "Found unexpected property (" << persistent_property_record.name() << ", "
+ << persistent_property_record.value() << ")";
+ expected.erase(it);
+ }
+ auto joiner = [](const std::vector<std::pair<std::string, std::string>>& vector) {
+ std::string result;
+ for (const auto& [name, value] : vector) {
+ result += " (" + name + ", " + value + ")";
+ }
+ return result;
+ };
+ EXPECT_TRUE(expected.empty()) << "Did not find expected properties:" << joiner(expected);
+}
+
+TEST(persistent_properties, EndToEnd) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ persistent_property_filename = tf.path;
+
+ std::vector<std::pair<std::string, std::string>> persistent_properties = {
+ {"persist.sys.locale", "en-US"},
+ {"persist.sys.timezone", "America/Los_Angeles"},
+ {"persist.test.empty.value", ""},
+ {"persist.test.new.line", "abc\n\n\nabc"},
+ {"persist.test.numbers", "1234567890"},
+ {"persist.test.non.ascii", "\x00\x01\x02\xFF\xFE\xFD\x7F\x8F\x9F"},
+ // We don't currently allow for non-ascii names for system properties, but this is a policy
+ // decision, not a technical limitation.
+ {"persist.\x00\x01\x02\xFF\xFE\xFD\x7F\x8F\x9F", "non-ascii-name"},
+ };
+
+ ASSERT_TRUE(WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
+
+ auto read_back_properties = LoadPersistentProperties();
+ CheckPropertiesEqual(persistent_properties, read_back_properties);
+}
+
+TEST(persistent_properties, AddProperty) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ persistent_property_filename = tf.path;
+
+ std::vector<std::pair<std::string, std::string>> persistent_properties = {
+ {"persist.sys.timezone", "America/Los_Angeles"},
+ };
+ ASSERT_TRUE(WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
+
+ WritePersistentProperty("persist.sys.locale", "pt-BR");
+
+ std::vector<std::pair<std::string, std::string>> persistent_properties_expected = {
+ {"persist.sys.timezone", "America/Los_Angeles"},
+ {"persist.sys.locale", "pt-BR"},
+ };
+
+ auto read_back_properties = LoadPersistentProperties();
+ CheckPropertiesEqual(persistent_properties_expected, read_back_properties);
+}
+
+TEST(persistent_properties, UpdateProperty) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ persistent_property_filename = tf.path;
+
+ std::vector<std::pair<std::string, std::string>> persistent_properties = {
+ {"persist.sys.locale", "en-US"},
+ {"persist.sys.timezone", "America/Los_Angeles"},
+ };
+ ASSERT_TRUE(WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));
+
+ WritePersistentProperty("persist.sys.locale", "pt-BR");
+
+ std::vector<std::pair<std::string, std::string>> persistent_properties_expected = {
+ {"persist.sys.locale", "pt-BR"},
+ {"persist.sys.timezone", "America/Los_Angeles"},
+ };
+
+ auto read_back_properties = LoadPersistentProperties();
+ CheckPropertiesEqual(persistent_properties_expected, read_back_properties);
+}
+
+TEST(persistent_properties, UpdatePropertyBadParse) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ persistent_property_filename = tf.path;
+
+ ASSERT_TRUE(WriteFile(tf.path, "ab"));
+
+ WritePersistentProperty("persist.sys.locale", "pt-BR");
+
+ auto read_back_properties = LoadPersistentProperties();
+ EXPECT_GT(read_back_properties.properties().size(), 0);
+
+ auto it =
+ std::find_if(read_back_properties.properties().begin(),
+ read_back_properties.properties().end(), [](const auto& entry) {
+ return entry.name() == "persist.sys.locale" && entry.value() == "pt-BR";
+ });
+ EXPECT_FALSE(it == read_back_properties.properties().end());
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/property_service.cpp b/init/property_service.cpp
index f0e4d2e..1a44fe3 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -17,7 +17,6 @@
#include "property_service.h"
#include <ctype.h>
-#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
@@ -34,6 +33,7 @@
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
+#include <wchar.h>
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
@@ -46,6 +46,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <bootimg.h>
#include <fs_mgr.h>
@@ -54,17 +55,19 @@
#include <selinux/selinux.h>
#include "init.h"
+#include "persistent_properties.h"
#include "util.h"
+using android::base::StartsWith;
+using android::base::StringPrintf;
using android::base::Timer;
-#define PERSISTENT_PROPERTY_DIR "/data/property"
#define RECOVERY_MOUNT_POINT "/recovery"
namespace android {
namespace init {
-static int persistent_properties_loaded = 0;
+static bool persistent_properties_loaded = false;
static int property_set_fd = -1;
@@ -72,8 +75,7 @@
void property_init() {
if (__system_property_area_init()) {
- LOG(ERROR) << "Failed to initialize property area";
- exit(1);
+ LOG(FATAL) << "Failed to initialize property area";
}
}
@@ -120,29 +122,6 @@
return check_mac_perms(ctl_name, sctx, cr);
}
-static void write_persistent_property(const char *name, const char *value)
-{
- char tempPath[PATH_MAX];
- char path[PATH_MAX];
- int fd;
-
- snprintf(tempPath, sizeof(tempPath), "%s/.temp.XXXXXX", PERSISTENT_PROPERTY_DIR);
- fd = mkstemp(tempPath);
- if (fd < 0) {
- PLOG(ERROR) << "Unable to write persistent property to temp file " << tempPath;
- return;
- }
- write(fd, value, strlen(value));
- fsync(fd);
- close(fd);
-
- snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name);
- if (rename(tempPath, path)) {
- PLOG(ERROR) << "Unable to rename persistent property file " << tempPath << " to " << path;
- unlink(tempPath);
- }
-}
-
bool is_legal_property_name(const std::string& name) {
size_t namelen = name.size();
@@ -176,16 +155,22 @@
return PROP_ERROR_INVALID_NAME;
}
- if (valuelen >= PROP_VALUE_MAX) {
+ if (valuelen >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {
LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
<< "value too long";
return PROP_ERROR_INVALID_VALUE;
}
+ if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
+ LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
+ << "value not a UTF8 encoded string";
+ return PROP_ERROR_INVALID_VALUE;
+ }
+
prop_info* pi = (prop_info*) __system_property_find(name.c_str());
if (pi != nullptr) {
// ro.* properties are actually "write-once".
- if (android::base::StartsWith(name, "ro.")) {
+ if (StartsWith(name, "ro.")) {
LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
<< "property already set";
return PROP_ERROR_READ_ONLY_PROPERTY;
@@ -203,8 +188,8 @@
// Don't write properties to disk until after we have read all default
// properties to prevent them from being overwritten by default values.
- if (persistent_properties_loaded && android::base::StartsWith(name, "persist.")) {
- write_persistent_property(name.c_str(), value.c_str());
+ if (persistent_properties_loaded && StartsWith(name, "persist.")) {
+ WritePersistentProperty(name, value);
}
property_changed(name, value);
return PROP_SUCCESS;
@@ -238,7 +223,7 @@
LOG(ERROR) << "property_set_async(\"" << info.name << "\", \"" << info.value
<< "\") failed";
}
- exit(0);
+ _exit(0);
}
}
@@ -424,7 +409,7 @@
char* source_ctx = nullptr;
getpeercon(socket.socket(), &source_ctx);
- if (android::base::StartsWith(name, "ctl.")) {
+ if (StartsWith(name, "ctl.")) {
if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
handle_control_message(name.c_str() + 4, value.c_str());
if (!legacy_protocol) {
@@ -442,6 +427,20 @@
}
} else {
if (check_mac_perms(name, source_ctx, &cr)) {
+ // sys.powerctl is a special property that is used to make the device reboot. We want to log
+ // any process that sets this property to be able to accurately blame the cause of a shutdown.
+ if (name == "sys.powerctl") {
+ std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
+ std::string process_cmdline;
+ std::string process_log_string;
+ if (android::base::ReadFileToString(cmdline_path, &process_cmdline)) {
+ // Since cmdline is null deliminated, .c_str() conveniently gives us just the process path.
+ process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
+ }
+ LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
+ << process_log_string;
+ }
+
uint32_t result = property_set(name, value);
if (!legacy_protocol) {
socket.SendUint32(result);
@@ -599,61 +598,6 @@
LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
}
-static void load_persistent_properties() {
- persistent_properties_loaded = 1;
-
- std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(PERSISTENT_PROPERTY_DIR), closedir);
- if (!dir) {
- PLOG(ERROR) << "Unable to open persistent property directory \""
- << PERSISTENT_PROPERTY_DIR << "\"";
- return;
- }
-
- struct dirent* entry;
- while ((entry = readdir(dir.get())) != NULL) {
- if (strncmp("persist.", entry->d_name, strlen("persist."))) {
- continue;
- }
- if (entry->d_type != DT_REG) {
- continue;
- }
-
- // Open the file and read the property value.
- int fd = openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW);
- if (fd == -1) {
- PLOG(ERROR) << "Unable to open persistent property file \"" << entry->d_name << "\"";
- continue;
- }
-
- struct stat sb;
- if (fstat(fd, &sb) == -1) {
- PLOG(ERROR) << "fstat on property file \"" << entry->d_name << "\" failed";
- close(fd);
- continue;
- }
-
- // File must not be accessible to others, be owned by root/root, and
- // not be a hard link to any other file.
- if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || sb.st_uid != 0 || sb.st_gid != 0 || sb.st_nlink != 1) {
- PLOG(ERROR) << "skipping insecure property file " << entry->d_name
- << " (uid=" << sb.st_uid << " gid=" << sb.st_gid
- << " nlink=" << sb.st_nlink << " mode=" << std::oct << sb.st_mode << ")";
- close(fd);
- continue;
- }
-
- char value[PROP_VALUE_MAX];
- int length = read(fd, value, sizeof(value) - 1);
- if (length >= 0) {
- value[length] = 0;
- property_set(entry->d_name, value);
- } else {
- PLOG(ERROR) << "Unable to read persistent property file " << entry->d_name;
- }
- close(fd);
- }
-}
-
// persist.sys.usb.config values can't be combined on build-time when property
// files are split into each partition.
// So we need to apply the same rule of build/make/tools/post_process_props.py
@@ -703,7 +647,11 @@
load_override_properties();
/* Read persistent properties after all default values have been loaded. */
- load_persistent_properties();
+ auto persistent_properties = LoadPersistentProperties();
+ for (const auto& persistent_property_record : persistent_properties.properties()) {
+ property_set(persistent_property_record.name(), persistent_property_record.value());
+ }
+ persistent_properties_loaded = true;
property_set("ro.persistent_properties.ready", "true");
}
@@ -771,8 +719,7 @@
property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
false, 0666, 0, 0, nullptr);
if (property_set_fd == -1) {
- PLOG(ERROR) << "start_property_service socket creation failed";
- exit(1);
+ PLOG(FATAL) << "start_property_service socket creation failed";
}
listen(property_set_fd, 8);
diff --git a/init/property_service_test.cpp b/init/property_service_test.cpp
index 3a64e02..95dd340 100644
--- a/init/property_service_test.cpp
+++ b/init/property_service_test.cpp
@@ -21,8 +21,11 @@
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
+#include <android-base/properties.h>
#include <gtest/gtest.h>
+using android::base::SetProperty;
+
namespace android {
namespace init {
@@ -50,5 +53,19 @@
ASSERT_EQ(0, close(fd));
}
+TEST(property_service, non_utf8_value) {
+ ASSERT_TRUE(SetProperty("property_service_utf8_test", "base_success"));
+ EXPECT_FALSE(SetProperty("property_service_utf8_test", "\x80"));
+ EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xC2\x01"));
+ EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xE0\xFF"));
+ EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xE0\xA0\xFF"));
+ EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xF0\x01\xFF"));
+ EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xF0\x90\xFF"));
+ EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xF0\x90\x80\xFF"));
+ EXPECT_FALSE(SetProperty("property_service_utf8_test", "\xF0\x90\x80"));
+ EXPECT_FALSE(SetProperty("property_service_utf8_test", "ab\xF0\x90\x80\x80qe\xF0\x90\x80"));
+ EXPECT_TRUE(SetProperty("property_service_utf8_test", "\xF0\x90\x80\x80"));
+}
+
} // namespace init
} // namespace android
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 891ca03..b17dbaf 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -54,8 +54,9 @@
#include "init.h"
#include "property_service.h"
#include "service.h"
-#include "signal_handler.h"
+#include "sigchld_handler.h"
+using android::base::Split;
using android::base::StringPrintf;
using android::base::Timer;
@@ -86,8 +87,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;
@@ -169,9 +170,7 @@
<< stat;
}
-// Determines whether the system is capable of rebooting. This is conservative,
-// so if any of the attempts to determine this fail, it will still return true.
-static bool IsRebootCapable() {
+bool IsRebootCapable() {
if (!CAP_IS_SUPPORTED(CAP_SYS_BOOT)) {
PLOG(WARNING) << "CAP_SYS_BOOT is not supported";
return true;
@@ -282,14 +281,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;
}
@@ -347,7 +347,16 @@
Timer t;
LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
- property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str());
+ // Ensure last reboot reason is reduced to canonical
+ // alias reported in bootloader or system boot reason.
+ size_t skip = 0;
+ std::vector<std::string> reasons = Split(reason, ",");
+ if (reasons.size() >= 2 && reasons[0] == "reboot" &&
+ (reasons[1] == "recovery" || reasons[1] == "bootloader" || reasons[1] == "cold" ||
+ reasons[1] == "hard" || reasons[1] == "warm")) {
+ skip = strlen("reboot,");
+ }
+ property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str() + skip);
sync();
bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;
@@ -470,7 +479,7 @@
bool HandlePowerctlMessage(const std::string& command) {
unsigned int cmd = 0;
- std::vector<std::string> cmd_params = android::base::Split(command, ",");
+ std::vector<std::string> cmd_params = Split(command, ",");
std::string reboot_target = "";
bool run_fsck = false;
bool command_invalid = false;
@@ -485,6 +494,8 @@
// Run fsck once the file system is remounted in read-only mode.
run_fsck = true;
} else if (cmd_params[1] == "thermal") {
+ // Turn off sources of heat immediately.
+ TurnOffBacklight();
// run_fsck is false to avoid delay
cmd = ANDROID_RB_THERMOFF;
}
@@ -521,8 +532,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/reboot.h b/init/reboot.h
index ece407f..1c58bd1 100644
--- a/init/reboot.h
+++ b/init/reboot.h
@@ -38,6 +38,10 @@
// Parses and handles a setprop sys.powerctl message.
bool HandlePowerctlMessage(const std::string& command);
+// Determines whether the system is capable of rebooting. This is conservative,
+// so if any of the attempts to determine this fail, it will still return true.
+bool IsRebootCapable();
+
} // namespace init
} // namespace android
diff --git a/init/result.h b/init/result.h
index 36c3b83..fc03962 100644
--- a/init/result.h
+++ b/init/result.h
@@ -153,8 +153,14 @@
template <typename T>
class Result {
public:
- template <typename... U>
- Result(U&&... result) : contents_(std::in_place_index_t<0>(), std::forward<U>(result)...) {}
+ Result() {}
+
+ template <typename U, typename... V,
+ typename = std::enable_if_t<!(std::is_same_v<std::decay_t<U>, Result<T>> &&
+ sizeof...(V) == 0)>>
+ Result(U&& result, V&&... results)
+ : contents_(std::in_place_index_t<0>(), std::forward<U>(result),
+ std::forward<V>(results)...) {}
Result(Error&& error) : contents_(std::in_place_index_t<1>(), error.str(), error.get_errno()) {}
Result(const ResultError& result_error)
diff --git a/init/result_test.cpp b/init/result_test.cpp
index 19caaf5..327b444 100644
--- a/init/result_test.cpp
+++ b/init/result_test.cpp
@@ -276,6 +276,49 @@
EXPECT_EQ(0U, ConstructorTracker::move_assignment_called);
}
+// Below two tests require that we do not hide the move constructor with our forwarding reference
+// constructor. This is done with by disabling the forwarding reference constructor if its first
+// and only type is Result<T>.
+TEST(result, result_result_with_success) {
+ auto return_result_result_with_success = []() -> Result<Result<Success>> {
+ return Result<Success>();
+ };
+ auto result = return_result_result_with_success();
+ ASSERT_TRUE(result);
+ ASSERT_TRUE(*result);
+
+ auto inner_result = result.value();
+ ASSERT_TRUE(inner_result);
+}
+
+TEST(result, result_result_with_failure) {
+ auto return_result_result_with_error = []() -> Result<Result<Success>> {
+ return Result<Success>(ResultError("failure string", 6));
+ };
+ auto result = return_result_result_with_error();
+ ASSERT_TRUE(result);
+ ASSERT_FALSE(*result);
+ EXPECT_EQ("failure string", result->error_string());
+ EXPECT_EQ(6, result->error_errno());
+}
+
+// This test requires that we disable the forwarding reference constructor if Result<T> is the
+// *only* type that we are forwarding. In otherwords, if we are forwarding Result<T>, int to
+// construct a Result<T>, then we still need the constructor.
+TEST(result, result_two_parameter_constructor_same_type) {
+ struct TestStruct {
+ TestStruct(int value) : value_(value) {}
+ TestStruct(Result<TestStruct> result, int value) : value_(result->value_ * value) {}
+ int value_;
+ };
+
+ auto return_test_struct = []() -> Result<TestStruct> { return {Result<TestStruct>(6), 6}; };
+
+ auto result = return_test_struct();
+ ASSERT_TRUE(result);
+ EXPECT_EQ(36, result->value_);
+}
+
TEST(result, die_on_access_failed_result) {
Result<std::string> result = Error();
ASSERT_DEATH(*result, "");
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 ef59164..c4b01e6 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -198,9 +198,18 @@
bool FindPrecompiledSplitPolicy(std::string* file) {
file->clear();
-
- static constexpr const char precompiled_sepolicy[] = "/vendor/etc/selinux/precompiled_sepolicy";
- if (access(precompiled_sepolicy, R_OK) == -1) {
+ // If there is an odm partition, precompiled_sepolicy will be in
+ // odm/etc/selinux. Otherwise it will be in vendor/etc/selinux.
+ static constexpr const char vendor_precompiled_sepolicy[] =
+ "/vendor/etc/selinux/precompiled_sepolicy";
+ static constexpr const char odm_precompiled_sepolicy[] =
+ "/odm/etc/selinux/precompiled_sepolicy";
+ if (access(odm_precompiled_sepolicy, R_OK) == 0) {
+ *file = odm_precompiled_sepolicy;
+ } else if (access(vendor_precompiled_sepolicy, R_OK) == 0) {
+ *file = vendor_precompiled_sepolicy;
+ } else {
+ PLOG(INFO) << "No precompiled sepolicy";
return false;
}
std::string actual_plat_id;
@@ -209,19 +218,18 @@
"/system/etc/selinux/plat_and_mapping_sepolicy.cil.sha256";
return false;
}
+
std::string precompiled_plat_id;
- if (!ReadFirstLine("/vendor/etc/selinux/precompiled_sepolicy.plat_and_mapping.sha256",
- &precompiled_plat_id)) {
- PLOG(INFO) << "Failed to read "
- "/vendor/etc/selinux/"
- "precompiled_sepolicy.plat_and_mapping.sha256";
+ std::string precompiled_sha256 = *file + ".plat_and_mapping.sha256";
+ if (!ReadFirstLine(precompiled_sha256.c_str(), &precompiled_plat_id)) {
+ PLOG(INFO) << "Failed to read " << precompiled_sha256;
+ file->clear();
return false;
}
if ((actual_plat_id.empty()) || (actual_plat_id != precompiled_plat_id)) {
+ file->clear();
return false;
}
-
- *file = precompiled_sepolicy;
return true;
}
@@ -293,24 +301,55 @@
return false;
}
std::string mapping_file("/system/etc/selinux/mapping/" + vend_plat_vers + ".cil");
+
+ // vendor_sepolicy.cil and nonplat_declaration.cil are the new design to replace
+ // nonplat_sepolicy.cil.
+ std::string nonplat_declaration_cil_file("/vendor/etc/selinux/nonplat_declaration.cil");
+ std::string vendor_policy_cil_file("/vendor/etc/selinux/vendor_sepolicy.cil");
+
+ if (access(vendor_policy_cil_file.c_str(), F_OK) == -1) {
+ // For backward compatibility.
+ // TODO: remove this after no device is using nonplat_sepolicy.cil.
+ vendor_policy_cil_file = "/vendor/etc/selinux/nonplat_sepolicy.cil";
+ nonplat_declaration_cil_file.clear();
+ } else if (access(nonplat_declaration_cil_file.c_str(), F_OK) == -1) {
+ LOG(ERROR) << "Missing " << nonplat_declaration_cil_file;
+ return false;
+ }
+
+ // odm_sepolicy.cil is default but optional.
+ std::string odm_policy_cil_file("/odm/etc/selinux/odm_sepolicy.cil");
+ if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {
+ odm_policy_cil_file.clear();
+ }
const std::string version_as_string = std::to_string(max_policy_version);
// clang-format off
- const char* compile_args[] = {
+ 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(),
- "/vendor/etc/selinux/nonplat_sepolicy.cil",
"-o", compiled_sepolicy,
// We don't care about file_contexts output by the compiler
"-f", "/sys/fs/selinux/null", // /dev/null is not yet available
- nullptr};
+ };
// clang-format on
- if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args)) {
+ if (!nonplat_declaration_cil_file.empty()) {
+ compile_args.push_back(nonplat_declaration_cil_file.c_str());
+ }
+ if (!vendor_policy_cil_file.empty()) {
+ compile_args.push_back(vendor_policy_cil_file.c_str());
+ }
+ if (!odm_policy_cil_file.empty()) {
+ compile_args.push_back(odm_policy_cil_file.c_str());
+ }
+ compile_args.push_back(nullptr);
+
+ if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args.data())) {
unlink(compiled_sepolicy);
return false;
}
diff --git a/init/service.cpp b/init/service.cpp
index 6f27a4b..b339bc0 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -135,29 +135,34 @@
}
}
-static void ExpandArgs(const std::vector<std::string>& args, std::vector<char*>* strs) {
+static bool ExpandArgsAndExecv(const std::vector<std::string>& args) {
std::vector<std::string> expanded_args;
+ std::vector<char*> c_strings;
+
expanded_args.resize(args.size());
- strs->push_back(const_cast<char*>(args[0].c_str()));
+ c_strings.push_back(const_cast<char*>(args[0].data()));
for (std::size_t i = 1; i < args.size(); ++i) {
if (!expand_props(args[i], &expanded_args[i])) {
LOG(FATAL) << args[0] << ": cannot expand '" << args[i] << "'";
}
- strs->push_back(const_cast<char*>(expanded_args[i].c_str()));
+ c_strings.push_back(expanded_args[i].data());
}
- strs->push_back(nullptr);
+ c_strings.push_back(nullptr);
+
+ return execv(c_strings[0], c_strings.data()) == 0;
}
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),
@@ -169,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),
@@ -785,10 +790,8 @@
// priority. Aborts on failure.
SetProcessAttributes();
- std::vector<char*> strs;
- ExpandArgs(args_, &strs);
- if (execv(strs[0], (char**)&strs[0]) < 0) {
- PLOG(ERROR) << "cannot execve('" << strs[0] << "')";
+ if (!ExpandArgsAndExecv(args_)) {
+ PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
}
_exit(127);
@@ -1005,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.
@@ -1053,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/signal_handler.cpp b/init/sigchld_handler.cpp
similarity index 79%
rename from init/signal_handler.cpp
rename to init/sigchld_handler.cpp
index 9e49c48..072a0fb 100644
--- a/init/signal_handler.cpp
+++ b/init/sigchld_handler.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "signal_handler.h"
+#include "sigchld_handler.h"
#include <signal.h>
#include <string.h>
@@ -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;
@@ -115,12 +121,11 @@
}
}
-void signal_handler_init() {
+void sigchld_handler_init() {
// Create a signalling mechanism for SIGCHLD.
int s[2];
if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0, s) == -1) {
- PLOG(ERROR) << "socketpair failed";
- exit(1);
+ PLOG(FATAL) << "socketpair failed in sigchld_handler_init";
}
signal_write_fd = s[0];
diff --git a/init/signal_handler.h b/init/sigchld_handler.h
similarity index 88%
rename from init/signal_handler.h
rename to init/sigchld_handler.h
index 9362be5..c86dc8d 100644
--- a/init/signal_handler.h
+++ b/init/sigchld_handler.h
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-#ifndef _INIT_SIGNAL_HANDLER_H_
-#define _INIT_SIGNAL_HANDLER_H_
+#ifndef _INIT_SIGCHLD_HANDLER_H_
+#define _INIT_SIGCHLD_HANDLER_H_
namespace android {
namespace init {
void ReapAnyOutstandingChildren();
-void signal_handler_init(void);
+void sigchld_handler_init(void);
} // namespace init
} // namespace android
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 3b2f38e..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,12 +38,13 @@
#include <unwindstack/Regs.h>
#include <unwindstack/RegsGetLocal.h>
+#include <unwindstack/Unwinder.h>
+
#include "BacktraceLog.h"
#include "UnwindStack.h"
#include "UnwindStackMap.h"
-static std::string GetFunctionName(pid_t pid, BacktraceMap* back_map, uintptr_t pc,
- uintptr_t* offset) {
+static std::string GetFunctionName(BacktraceMap* back_map, uintptr_t pc, uintptr_t* offset) {
*offset = 0;
unwindstack::Maps* maps = reinterpret_cast<UnwindStackMap*>(back_map)->stack_maps();
@@ -52,7 +54,8 @@
return "";
}
- unwindstack::Elf* elf = map_info->GetElf(pid, true);
+ UnwindStackMap* stack_map = reinterpret_cast<UnwindStackMap*>(back_map);
+ unwindstack::Elf* elf = map_info->GetElf(stack_map->process_memory(), true);
std::string name;
uint64_t func_offset;
@@ -63,93 +66,52 @@
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 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);
+ auto process_memory = stack_map->process_memory();
+ unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(),
+ regs, stack_map->process_memory());
+ unwinder.Unwind(&skip_names);
-static bool Unwind(pid_t pid, unwindstack::Memory* memory, unwindstack::Regs* regs,
- BacktraceMap* back_map, std::vector<backtrace_frame_data_t>* frames,
- size_t num_ignore_frames) {
- unwindstack::Maps* maps = reinterpret_cast<UnwindStackMap*>(back_map)->stack_maps();
- bool adjust_rel_pc = false;
- size_t num_frames = 0;
- frames->clear();
- while (num_frames < MAX_BACKTRACE_FRAMES) {
- if (regs->pc() == 0) {
- break;
- }
- unwindstack::MapInfo* map_info = maps->Find(regs->pc());
- if (map_info == nullptr) {
- break;
- }
+ if (num_ignore_frames >= unwinder.NumFrames()) {
+ frames->resize(0);
+ return true;
+ }
- unwindstack::Elf* elf = map_info->GetElf(pid, true);
- uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
+ 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);
- bool skip_frame = num_frames == 0 && IsUnwindLibrary(map_info->name);
- if (num_ignore_frames == 0 && !skip_frame) {
- uint64_t adjusted_rel_pc = rel_pc;
- if (adjust_rel_pc) {
- adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
- }
- frames->resize(num_frames + 1);
- backtrace_frame_data_t* frame = &frames->at(num_frames);
- frame->num = num_frames;
- // 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;
+ back_frame->num = frame->num;
- frame->map.start = map_info->start;
- frame->map.end = map_info->end;
- frame->map.offset = map_info->offset;
- frame->map.load_bias = elf->GetLoadBias();
- frame->map.flags = map_info->flags;
- frame->map.name = map_info->name;
+ back_frame->rel_pc = frame->rel_pc;
+ back_frame->pc = frame->pc;
+ back_frame->sp = frame->sp;
- 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;
- 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 if (!skip_frame && num_ignore_frames > 0) {
- num_ignore_frames--;
- }
- adjust_rel_pc = true;
+ back_frame->func_name = frame->function_name;
+ back_frame->func_offset = frame->function_offset;
- // Do not unwind through a device map.
- if (map_info->flags & PROT_DEVICE_MAP) {
- break;
- }
- unwindstack::MapInfo* sp_info = maps->Find(regs->sp());
- if (sp_info->flags & PROT_DEVICE_MAP) {
- break;
- }
-
- if (!elf->Step(rel_pc + map_info->elf_offset, regs, memory)) {
- break;
- }
+ 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;
}
UnwindStackCurrent::UnwindStackCurrent(pid_t pid, pid_t tid, BacktraceMap* map)
- : BacktraceCurrent(pid, tid, map), memory_(new unwindstack::MemoryLocal) {}
+ : BacktraceCurrent(pid, tid, map) {}
std::string UnwindStackCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
- return ::GetFunctionName(Pid(), GetMap(), pc, offset);
+ return ::GetFunctionName(GetMap(), pc, offset);
}
bool UnwindStackCurrent::UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) {
@@ -165,14 +127,14 @@
}
error_ = BACKTRACE_UNWIND_NO_ERROR;
- return ::Unwind(getpid(), memory_.get(), regs.get(), GetMap(), &frames_, num_ignore_frames);
+ return ::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames);
}
UnwindStackPtrace::UnwindStackPtrace(pid_t pid, pid_t tid, BacktraceMap* map)
- : BacktracePtrace(pid, tid, map), memory_(new unwindstack::MemoryRemote(pid)) {}
+ : BacktracePtrace(pid, tid, map) {}
std::string UnwindStackPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
- return ::GetFunctionName(Pid(), GetMap(), pc, offset);
+ return ::GetFunctionName(GetMap(), pc, offset);
}
bool UnwindStackPtrace::Unwind(size_t num_ignore_frames, ucontext_t* context) {
@@ -185,7 +147,7 @@
}
error_ = BACKTRACE_UNWIND_NO_ERROR;
- return ::Unwind(Pid(), memory_.get(), regs.get(), GetMap(), &frames_, num_ignore_frames);
+ return ::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames);
}
Backtrace* Backtrace::CreateNew(pid_t pid, pid_t tid, BacktraceMap* map) {
diff --git a/libbacktrace/UnwindStack.h b/libbacktrace/UnwindStack.h
index 32d1f51..be9ef63 100644
--- a/libbacktrace/UnwindStack.h
+++ b/libbacktrace/UnwindStack.h
@@ -35,9 +35,6 @@
std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) override;
bool UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucontext) override;
-
- private:
- std::unique_ptr<unwindstack::Memory> memory_;
};
class UnwindStackPtrace : public BacktracePtrace {
@@ -48,9 +45,6 @@
bool Unwind(size_t num_ignore_frames, ucontext_t* context) override;
std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
-
- private:
- std::unique_ptr<unwindstack::Memory> memory_;
};
#endif // _LIBBACKTRACE_UNWIND_STACK_H
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index ba9fd87..d4a2444 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -36,6 +36,9 @@
stack_maps_.reset(new unwindstack::RemoteMaps(pid_));
}
+ // Create the process memory object.
+ process_memory_ = unwindstack::Memory::CreateProcessMemory(pid_);
+
if (!stack_maps_->Parse()) {
return false;
}
@@ -68,7 +71,7 @@
if (map_info == nullptr) {
return;
}
- unwindstack::Elf* elf = map_info->GetElf(pid_, true);
+ unwindstack::Elf* elf = map_info->GetElf(process_memory_, true);
map->load_bias = elf->GetLoadBias();
}
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
index 7885b74..b93b340 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -20,6 +20,8 @@
#include <stdint.h>
#include <sys/types.h>
+#include <memory>
+
#include <backtrace/BacktraceMap.h>
#include <unwindstack/Maps.h>
@@ -34,8 +36,11 @@
unwindstack::Maps* stack_maps() { return stack_maps_.get(); }
+ const std::shared_ptr<unwindstack::Memory>& process_memory() { return process_memory_; }
+
protected:
std::unique_ptr<unwindstack::Maps> stack_maps_;
+ std::shared_ptr<unwindstack::Memory> process_memory_;
};
#endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 9fe2d1c..e5eb9e3 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -82,6 +82,14 @@
int32_t done;
};
+typedef Backtrace* (*create_func_t)(pid_t, pid_t, BacktraceMap*);
+typedef BacktraceMap* (*map_create_func_t)(pid_t, bool);
+
+static void VerifyLevelDump(Backtrace* backtrace, create_func_t create_func = nullptr,
+ map_create_func_t map_func = nullptr);
+static void VerifyMaxDump(Backtrace* backtrace, create_func_t create_func = nullptr,
+ map_create_func_t map_func = nullptr);
+
static uint64_t NanoTime() {
struct timespec t = { 0, 0 };
clock_gettime(CLOCK_MONOTONIC, &t);
@@ -147,7 +155,7 @@
return found;
}
-static void VerifyLevelDump(Backtrace* backtrace) {
+static void VerifyLevelDump(Backtrace* backtrace, create_func_t, map_create_func_t) {
ASSERT_GT(backtrace->NumFrames(), static_cast<size_t>(0))
<< DumpFrames(backtrace);
ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
@@ -189,7 +197,7 @@
return (backtrace->NumFrames() == MAX_BACKTRACE_FRAMES);
}
-static void VerifyMaxDump(Backtrace* backtrace) {
+static void VerifyMaxDump(Backtrace* backtrace, create_func_t, map_create_func_t) {
ASSERT_EQ(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
<< DumpFrames(backtrace);
// Verify that the last frame is our recursive call.
@@ -251,10 +259,14 @@
static void VerifyIgnoreFrames(Backtrace* bt_all, Backtrace* bt_ign1, Backtrace* bt_ign2,
const char* cur_proc) {
- EXPECT_EQ(bt_all->NumFrames(), bt_ign1->NumFrames() + 1)
- << "All backtrace:\n" << DumpFrames(bt_all) << "Ignore 1 backtrace:\n" << DumpFrames(bt_ign1);
- EXPECT_EQ(bt_all->NumFrames(), bt_ign2->NumFrames() + 2)
- << "All backtrace:\n" << DumpFrames(bt_all) << "Ignore 2 backtrace:\n" << DumpFrames(bt_ign2);
+ ASSERT_EQ(bt_all->NumFrames(), bt_ign1->NumFrames() + 1) << "All backtrace:\n"
+ << DumpFrames(bt_all)
+ << "Ignore 1 backtrace:\n"
+ << DumpFrames(bt_ign1);
+ ASSERT_EQ(bt_all->NumFrames(), bt_ign2->NumFrames() + 2) << "All backtrace:\n"
+ << DumpFrames(bt_all)
+ << "Ignore 2 backtrace:\n"
+ << DumpFrames(bt_ign2);
// Check all of the frames are the same > the current frame.
bool check = (cur_proc == nullptr);
@@ -305,9 +317,8 @@
}
static void VerifyProcTest(pid_t pid, pid_t tid, bool (*ReadyFunc)(Backtrace*),
- void (*VerifyFunc)(Backtrace*),
- Backtrace* (*back_func)(pid_t, pid_t, BacktraceMap*),
- BacktraceMap* (*map_func)(pid_t, bool)) {
+ void (*VerifyFunc)(Backtrace*, create_func_t, map_create_func_t),
+ create_func_t create_func, map_create_func_t map_create_func) {
pid_t ptrace_tid;
if (tid < 0) {
ptrace_tid = pid;
@@ -324,13 +335,13 @@
WaitForStop(ptrace_tid);
std::unique_ptr<BacktraceMap> map;
- map.reset(map_func(pid, false));
- std::unique_ptr<Backtrace> backtrace(back_func(pid, tid, map.get()));
+ map.reset(map_create_func(pid, false));
+ std::unique_ptr<Backtrace> backtrace(create_func(pid, tid, map.get()));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
if (ReadyFunc(backtrace.get())) {
- VerifyFunc(backtrace.get());
+ VerifyFunc(backtrace.get(), create_func, map_create_func);
verified = true;
} else {
last_dump = DumpFrames(backtrace.get());
@@ -399,13 +410,15 @@
ASSERT_EQ(waitpid(pid, &status, 0), pid);
}
-static void VerifyProcessIgnoreFrames(Backtrace* bt_all) {
- std::unique_ptr<Backtrace> ign1(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
+static void VerifyProcessIgnoreFrames(Backtrace* bt_all, create_func_t create_func,
+ map_create_func_t map_create_func) {
+ std::unique_ptr<BacktraceMap> map(map_create_func(bt_all->Pid(), false));
+ std::unique_ptr<Backtrace> ign1(create_func(bt_all->Pid(), BACKTRACE_CURRENT_THREAD, map.get()));
ASSERT_TRUE(ign1.get() != nullptr);
ASSERT_TRUE(ign1->Unwind(1));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError());
- std::unique_ptr<Backtrace> ign2(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
+ std::unique_ptr<Backtrace> ign2(create_func(bt_all->Pid(), BACKTRACE_CURRENT_THREAD, map.get()));
ASSERT_TRUE(ign2.get() != nullptr);
ASSERT_TRUE(ign2->Unwind(2));
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError());
@@ -1702,9 +1715,8 @@
;
}
-static void UnwindThroughSignal(bool use_action,
- Backtrace* (*back_func)(pid_t, pid_t, BacktraceMap*),
- BacktraceMap* (*map_func)(pid_t, bool)) {
+static void UnwindThroughSignal(bool use_action, create_func_t create_func,
+ map_create_func_t map_create_func) {
volatile int value = 0;
pid_t pid;
if ((pid = fork()) == 0) {
@@ -1730,8 +1742,8 @@
WaitForStop(pid);
- std::unique_ptr<BacktraceMap> map(map_func(pid, false));
- std::unique_ptr<Backtrace> backtrace(back_func(pid, pid, map.get()));
+ std::unique_ptr<BacktraceMap> map(map_create_func(pid, false));
+ std::unique_ptr<Backtrace> backtrace(create_func(pid, pid, map.get()));
size_t bytes_read = backtrace->Read(reinterpret_cast<uintptr_t>(const_cast<int*>(&value)),
reinterpret_cast<uint8_t*>(&read_value), sizeof(read_value));
@@ -1758,9 +1770,9 @@
WaitForStop(pid);
- map.reset(map_func(pid, false));
+ map.reset(map_create_func(pid, false));
ASSERT_TRUE(map.get() != nullptr);
- backtrace.reset(back_func(pid, pid, map.get()));
+ backtrace.reset(create_func(pid, pid, map.get()));
ASSERT_TRUE(backtrace->Unwind(0));
bool found = false;
for (frame_iter = backtrace->begin(); frame_iter != backtrace->end(); ++frame_iter) {
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index d67ea50..289fd0c 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -127,7 +127,7 @@
// Create a string representing the formatted line of backtrace information
// for a single frame.
virtual std::string FormatFrameData(size_t frame_num);
- virtual std::string FormatFrameData(const backtrace_frame_data_t* frame);
+ static std::string FormatFrameData(const backtrace_frame_data_t* frame);
pid_t Pid() const { return pid_; }
pid_t Tid() const { return tid_; }
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index 963c34b..6cf8b3f 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -91,6 +91,8 @@
const_iterator begin() const { return maps_.begin(); }
const_iterator end() const { return maps_.end(); }
+ size_t size() const { return maps_.size(); }
+
virtual bool Build();
static inline bool IsValid(const backtrace_map_t& map) {
diff --git a/libcrypto_utils/Android.bp b/libcrypto_utils/Android.bp
index 4a5f2a7..47de12a 100644
--- a/libcrypto_utils/Android.bp
+++ b/libcrypto_utils/Android.bp
@@ -17,6 +17,9 @@
cc_library {
name: "libcrypto_utils",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
host_supported: true,
srcs: [
"android_pubkey.c",
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/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index bbaf5f4..d54eeae 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -79,7 +79,6 @@
{ 00775, AID_ROOT, AID_ROOT, 0, "data/preloads" },
{ 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" },
{ 00755, AID_ROOT, AID_SYSTEM, 0, "mnt" },
- { 00755, AID_ROOT, AID_ROOT, 0, "root" },
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin" },
{ 00777, AID_ROOT, AID_ROOT, 0, "sdcard" },
{ 00751, AID_ROOT, AID_SDCARD_R, 0, "storage" },
@@ -174,6 +173,8 @@
CAP_MASK_LONG(CAP_AUDIT_CONTROL) |
CAP_MASK_LONG(CAP_SETGID),
"system/bin/logd" },
+ { 00550, AID_SYSTEM, AID_LOG, CAP_MASK_LONG(CAP_SYSLOG),
+ "system/bin/bootstat" },
{ 00750, AID_ROOT, AID_SHELL, CAP_MASK_LONG(CAP_SETUID) |
CAP_MASK_LONG(CAP_SETGID),
"system/bin/run-as" },
diff --git a/libdiskconfig/Android.bp b/libdiskconfig/Android.bp
index 088981a..b92f086 100644
--- a/libdiskconfig/Android.bp
+++ b/libdiskconfig/Android.bp
@@ -1,6 +1,9 @@
cc_library {
name: "libdiskconfig",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"diskconfig.c",
"diskutils.c",
@@ -20,7 +23,7 @@
darwin: {
enabled: false,
},
- linux: {
+ linux_glibc: {
cflags: [
"-O2",
"-g",
diff --git a/libgrallocusage/Android.bp b/libgrallocusage/Android.bp
index 54bfee5..cf03868 100644
--- a/libgrallocusage/Android.bp
+++ b/libgrallocusage/Android.bp
@@ -14,6 +14,7 @@
cc_library_static {
name: "libgrallocusage",
+ vendor_available: true,
cppflags: [
"-Weverything",
"-Werror",
@@ -26,4 +27,5 @@
srcs: ["GrallocUsageConversion.cpp"],
export_include_dirs: ["include"],
shared_libs: ["android.hardware.graphics.allocator@2.0"],
+ header_libs: ["libhardware_headers"],
}
diff --git a/libion/include/ion/ion.h b/libion/include/ion/ion.h
index f47793d..a60d24e 100644
--- a/libion/include/ion/ion.h
+++ b/libion/include/ion/ion.h
@@ -41,6 +41,15 @@
int ion_share(int fd, ion_user_handle_t handle, int *share_fd);
int ion_import(int fd, int share_fd, ion_user_handle_t *handle);
+/**
+ * Add 4.12+ kernel ION interfaces here for forward compatibility
+ * This should be needed till the pre-4.12+ ION interfaces are backported.
+ */
+int ion_query_heap_cnt(int fd, int* cnt);
+int ion_query_get_heaps(int fd, int cnt, void* buffers);
+
+int ion_is_legacy(int fd);
+
__END_DECLS
#endif /* __SYS_CORE_ION_H */
diff --git a/libion/ion.c b/libion/ion.c
index 9aaa6f2..5836128 100644
--- a/libion/ion.c
+++ b/libion/ion.c
@@ -22,6 +22,7 @@
#include <errno.h>
#include <fcntl.h>
#include <linux/ion.h>
+#include <stdatomic.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
@@ -30,81 +31,89 @@
#include <unistd.h>
#include <ion/ion.h>
+#include "ion_4.12.h"
+
#include <log/log.h>
-int ion_open()
-{
+enum ion_version { ION_VERSION_UNKNOWN, ION_VERSION_MODERN, ION_VERSION_LEGACY };
+
+static atomic_int g_ion_version = ATOMIC_VAR_INIT(ION_VERSION_UNKNOWN);
+
+int ion_is_legacy(int fd) {
+ int version = atomic_load_explicit(&g_ion_version, memory_order_acquire);
+ if (version == ION_VERSION_UNKNOWN) {
+ /**
+ * Check for FREE IOCTL here; it is available only in the old
+ * kernels, not the new ones.
+ */
+ int err = ion_free(fd, (ion_user_handle_t)NULL);
+ version = (err == -ENOTTY) ? ION_VERSION_MODERN : ION_VERSION_LEGACY;
+ atomic_store_explicit(&g_ion_version, version, memory_order_release);
+ }
+ return version == ION_VERSION_LEGACY;
+}
+
+int ion_open() {
int fd = open("/dev/ion", O_RDONLY | O_CLOEXEC);
- if (fd < 0)
- ALOGE("open /dev/ion failed!\n");
+ if (fd < 0) ALOGE("open /dev/ion failed!\n");
+
return fd;
}
-int ion_close(int fd)
-{
+int ion_close(int fd) {
int ret = close(fd);
- if (ret < 0)
- return -errno;
+ if (ret < 0) return -errno;
return ret;
}
-static int ion_ioctl(int fd, int req, void *arg)
-{
+static int ion_ioctl(int fd, int req, void* arg) {
int ret = ioctl(fd, req, arg);
if (ret < 0) {
- ALOGE("ioctl %x failed with code %d: %s\n", req,
- ret, strerror(errno));
+ ALOGE("ioctl %x failed with code %d: %s\n", req, ret, strerror(errno));
return -errno;
}
return ret;
}
-int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask,
- unsigned int flags, ion_user_handle_t *handle)
-{
- int ret;
+int ion_alloc(int fd, size_t len, size_t align, unsigned int heap_mask, unsigned int flags,
+ ion_user_handle_t* handle) {
+ int ret = 0;
+
+ if ((handle == NULL) || (!ion_is_legacy(fd))) return -EINVAL;
+
struct ion_allocation_data data = {
- .len = len,
- .align = align,
- .heap_id_mask = heap_mask,
- .flags = flags,
+ .len = len, .align = align, .heap_id_mask = heap_mask, .flags = flags,
};
- if (handle == NULL)
- return -EINVAL;
-
ret = ion_ioctl(fd, ION_IOC_ALLOC, &data);
- if (ret < 0)
- return ret;
+ if (ret < 0) return ret;
+
*handle = data.handle;
+
return ret;
}
-int ion_free(int fd, ion_user_handle_t handle)
-{
+int ion_free(int fd, ion_user_handle_t handle) {
struct ion_handle_data data = {
.handle = handle,
};
return ion_ioctl(fd, ION_IOC_FREE, &data);
}
-int ion_map(int fd, ion_user_handle_t handle, size_t length, int prot,
- int flags, off_t offset, unsigned char **ptr, int *map_fd)
-{
+int ion_map(int fd, ion_user_handle_t handle, size_t length, int prot, int flags, off_t offset,
+ unsigned char** ptr, int* map_fd) {
+ if (!ion_is_legacy(fd)) return -EINVAL;
int ret;
- unsigned char *tmp_ptr;
+ unsigned char* tmp_ptr;
struct ion_fd_data data = {
.handle = handle,
};
- if (map_fd == NULL)
- return -EINVAL;
- if (ptr == NULL)
- return -EINVAL;
+ if (map_fd == NULL) return -EINVAL;
+ if (ptr == NULL) return -EINVAL;
ret = ion_ioctl(fd, ION_IOC_MAP, &data);
- if (ret < 0)
- return ret;
+ if (ret < 0) return ret;
if (data.fd < 0) {
ALOGE("map ioctl returned negative fd\n");
return -EINVAL;
@@ -119,19 +128,17 @@
return ret;
}
-int ion_share(int fd, ion_user_handle_t handle, int *share_fd)
-{
+int ion_share(int fd, ion_user_handle_t handle, int* share_fd) {
int ret;
struct ion_fd_data data = {
.handle = handle,
};
- if (share_fd == NULL)
- return -EINVAL;
+ if (!ion_is_legacy(fd)) return -EINVAL;
+ if (share_fd == NULL) return -EINVAL;
ret = ion_ioctl(fd, ION_IOC_SHARE, &data);
- if (ret < 0)
- return ret;
+ if (ret < 0) return ret;
if (data.fd < 0) {
ALOGE("share ioctl returned negative fd\n");
return -EINVAL;
@@ -140,40 +147,75 @@
return ret;
}
-int ion_alloc_fd(int fd, size_t len, size_t align, unsigned int heap_mask,
- unsigned int flags, int *handle_fd) {
+int ion_alloc_fd(int fd, size_t len, size_t align, unsigned int heap_mask, unsigned int flags,
+ int* handle_fd) {
ion_user_handle_t handle;
int ret;
- ret = ion_alloc(fd, len, align, heap_mask, flags, &handle);
- if (ret < 0)
- return ret;
- ret = ion_share(fd, handle, handle_fd);
- ion_free(fd, handle);
+ if (!ion_is_legacy(fd)) {
+ struct ion_new_allocation_data data = {
+ .len = len,
+ .heap_id_mask = heap_mask,
+ .flags = flags,
+ };
+
+ ret = ion_ioctl(fd, ION_IOC_NEW_ALLOC, &data);
+ if (ret < 0) return ret;
+ *handle_fd = data.fd;
+ } else {
+ ret = ion_alloc(fd, len, align, heap_mask, flags, &handle);
+ if (ret < 0) return ret;
+ ret = ion_share(fd, handle, handle_fd);
+ ion_free(fd, handle);
+ }
return ret;
}
-int ion_import(int fd, int share_fd, ion_user_handle_t *handle)
-{
+int ion_import(int fd, int share_fd, ion_user_handle_t* handle) {
int ret;
struct ion_fd_data data = {
.fd = share_fd,
};
- if (handle == NULL)
- return -EINVAL;
+ if (!ion_is_legacy(fd)) return -EINVAL;
+
+ if (handle == NULL) return -EINVAL;
ret = ion_ioctl(fd, ION_IOC_IMPORT, &data);
- if (ret < 0)
- return ret;
+ if (ret < 0) return ret;
*handle = data.handle;
return ret;
}
-int ion_sync_fd(int fd, int handle_fd)
-{
+int ion_sync_fd(int fd, int handle_fd) {
struct ion_fd_data data = {
.fd = handle_fd,
};
+
+ if (!ion_is_legacy(fd)) return -EINVAL;
+
return ion_ioctl(fd, ION_IOC_SYNC, &data);
}
+
+int ion_query_heap_cnt(int fd, int* cnt) {
+ int ret;
+ struct ion_heap_query query;
+
+ memset(&query, 0, sizeof(query));
+
+ ret = ion_ioctl(fd, ION_IOC_HEAP_QUERY, &query);
+ if (ret < 0) return ret;
+
+ *cnt = query.cnt;
+ return ret;
+}
+
+int ion_query_get_heaps(int fd, int cnt, void* buffers) {
+ int ret;
+ struct ion_heap_query query = {
+ .cnt = cnt, .heaps = (uintptr_t)buffers,
+ };
+
+ ret = ion_ioctl(fd, ION_IOC_HEAP_QUERY, &query);
+ return ret;
+}
diff --git a/libion/ion_4.12.h b/libion/ion_4.12.h
new file mode 100644
index 0000000..6ae79d4
--- /dev/null
+++ b/libion/ion_4.12.h
@@ -0,0 +1,125 @@
+/*
+ * Adapted from drivers/staging/android/uapi/ion.h
+ *
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _UAPI_LINUX_ION_NEW_H
+#define _UAPI_LINUX_ION_NEW_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8)
+
+/**
+ * DOC: Ion Userspace API
+ *
+ * create a client by opening /dev/ion
+ * most operations handled via following ioctls
+ *
+ */
+
+/**
+ * struct ion_new_allocation_data - metadata passed from userspace for allocations
+ * @len: size of the allocation
+ * @heap_id_mask: mask of heap ids to allocate from
+ * @flags: flags passed to heap
+ * @handle: pointer that will be populated with a cookie to use to
+ * refer to this allocation
+ *
+ * Provided by userspace as an argument to the ioctl - added _new to denote
+ * this belongs to the new ION interface.
+ */
+struct ion_new_allocation_data {
+ __u64 len;
+ __u32 heap_id_mask;
+ __u32 flags;
+ __u32 fd;
+ __u32 unused;
+};
+
+#define MAX_HEAP_NAME 32
+
+/**
+ * struct ion_heap_data - data about a heap
+ * @name - first 32 characters of the heap name
+ * @type - heap type
+ * @heap_id - heap id for the heap
+ */
+struct ion_heap_data {
+ char name[MAX_HEAP_NAME];
+ __u32 type;
+ __u32 heap_id;
+ __u32 reserved0;
+ __u32 reserved1;
+ __u32 reserved2;
+};
+
+/**
+ * struct ion_heap_query - collection of data about all heaps
+ * @cnt - total number of heaps to be copied
+ * @heaps - buffer to copy heap data
+ */
+struct ion_heap_query {
+ __u32 cnt; /* Total number of heaps to be copied */
+ __u32 reserved0; /* align to 64bits */
+ __u64 heaps; /* buffer to be populated */
+ __u32 reserved1;
+ __u32 reserved2;
+};
+
+#define ION_IOC_MAGIC 'I'
+
+/**
+ * DOC: ION_IOC_NEW_ALLOC - allocate memory
+ *
+ * Takes an ion_allocation_data struct and returns it with the handle field
+ * populated with the opaque handle for the allocation.
+ * TODO: This IOCTL will clash by design; however, only one of
+ * ION_IOC_ALLOC or ION_IOC_NEW_ALLOC paths will be exercised,
+ * so this should not conflict.
+ */
+#define ION_IOC_NEW_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_new_allocation_data)
+
+/**
+ * DOC: ION_IOC_FREE - free memory
+ *
+ * Takes an ion_handle_data struct and frees the handle.
+ *
+ * #define ION_IOC_FREE _IOWR(ION_IOC_MAGIC, 1, struct ion_handle_data)
+ * This will come from the older kernels, so don't redefine here
+ */
+
+/**
+ * DOC: ION_IOC_SHARE - creates a file descriptor to use to share an allocation
+ *
+ * Takes an ion_fd_data struct with the handle field populated with a valid
+ * opaque handle. Returns the struct with the fd field set to a file
+ * descriptor open in the current address space. This file descriptor
+ * can then be passed to another process. The corresponding opaque handle can
+ * be retrieved via ION_IOC_IMPORT.
+ *
+ * #define ION_IOC_SHARE _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)
+ * This will come from the older kernels, so don't redefine here
+ */
+
+/**
+ * DOC: ION_IOC_HEAP_QUERY - information about available heaps
+ *
+ * Takes an ion_heap_query structure and populates information about
+ * available Ion heaps.
+ */
+#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, struct ion_heap_query)
+
+#endif /* _UAPI_LINUX_ION_NEW_H */
diff --git a/liblog/Android.bp b/liblog/Android.bp
index e74aa82..d5bb29e 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -42,6 +42,24 @@
"logd_writer.c",
]
+cc_library_headers {
+ name: "liblog_headers",
+ host_supported: true,
+ vendor_available: true,
+ export_include_dirs: ["include"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ linux_bionic: {
+ enabled: true,
+ },
+ vendor: {
+ export_include_dirs: ["include_vndk"],
+ },
+ },
+}
+
// Shared and static library for host and device
// ========================================================
cc_library {
@@ -73,15 +91,13 @@
not_windows: {
srcs: ["event_tag_map.cpp"],
},
- linux: {
- host_ldlibs: ["-lrt"],
- },
linux_bionic: {
enabled: true,
},
},
- export_include_dirs: ["include"],
+ header_libs: ["liblog_headers"],
+ export_header_lib_headers: ["liblog_headers"],
cflags: [
"-Werror",
@@ -100,7 +116,7 @@
}
ndk_headers {
- name: "liblog_headers",
+ name: "liblog_ndk_headers",
from: "include/android",
to: "android",
srcs: ["include/android/log.h"],
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h
index 5a3f04c..339a06d 100644
--- a/liblog/include/log/log_main.h
+++ b/liblog/include/log/log_main.h
@@ -18,10 +18,9 @@
#define _LIBS_LOG_LOG_MAIN_H
#include <android/log.h>
+#include <sys/cdefs.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
+__BEGIN_DECLS
/*
* Normally we strip the effects of ALOGV (VERBOSE messages),
@@ -385,8 +384,6 @@
#pragma clang diagnostic pop
#endif
-#ifdef __cplusplus
-}
-#endif
+__END_DECLS
#endif /* _LIBS_LOG_LOG_MAIN_H */
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/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
index 3764faf..309f5d1 100644
--- a/liblog/include/log/log_time.h
+++ b/liblog/include/log/log_time.h
@@ -28,6 +28,10 @@
#ifndef __struct_log_time_defined
#define __struct_log_time_defined
+#define LOG_TIME_SEC(t) ((t)->tv_sec)
+/* next power of two after NS_PER_SEC */
+#define LOG_TIME_NSEC(t) ((t)->tv_nsec & (UINT32_MAX >> 2))
+
#ifdef __cplusplus
/*
@@ -167,15 +171,15 @@
#endif
} __attribute__((__packed__));
-#else
+#else /* __cplusplus */
typedef struct log_time {
uint32_t tv_sec;
uint32_t tv_nsec;
} __attribute__((__packed__)) log_time;
-#endif
+#endif /* __cplusplus */
-#endif
+#endif /* __struct_log_time_defined */
#endif /* _LIBS_LOG_LOG_TIME_H */
diff --git a/liblog/include_vndk/log/log_time.h b/liblog/include_vndk/log/log_time.h
deleted file mode 120000
index abfe439..0000000
--- a/liblog/include_vndk/log/log_time.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/log/log_time.h
\ No newline at end of file
diff --git a/liblog/include_vndk/log/log_time.h b/liblog/include_vndk/log/log_time.h
new file mode 100644
index 0000000..5a09959
--- /dev/null
+++ b/liblog/include_vndk/log/log_time.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2005-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 _LIBS_LOG_LOG_TIME_H
+#define _LIBS_LOG_LOG_TIME_H
+
+#include <stdint.h>
+
+/* struct log_time is a wire-format variant of struct timespec */
+#ifndef NS_PER_SEC
+#define NS_PER_SEC 1000000000ULL
+#endif
+#ifndef US_PER_SEC
+#define US_PER_SEC 1000000ULL
+#endif
+#ifndef MS_PER_SEC
+#define MS_PER_SEC 1000ULL
+#endif
+
+#ifndef __struct_log_time_defined
+#define __struct_log_time_defined
+
+#define LOG_TIME_SEC(t) ((t)->tv_sec)
+/* next power of two after NS_PER_SEC */
+#define LOG_TIME_NSEC(t) ((t)->tv_nsec & (UINT32_MAX >> 2))
+
+typedef struct log_time {
+ uint32_t tv_sec;
+ uint32_t tv_nsec;
+} __attribute__((__packed__)) log_time;
+
+#endif
+
+#endif /* _LIBS_LOG_LOG_TIME_H */
diff --git a/liblog/local_logger.c b/liblog/local_logger.c
index 522867d..563cb3f 100644
--- a/liblog/local_logger.c
+++ b/liblog/local_logger.c
@@ -222,6 +222,7 @@
log->last[logId] = node->prev;
}
list_remove(node);
+ LOG_ALWAYS_FATAL_IF(node == log->last[logId], "corrupted list");
free(e);
}
/* add entry to list */
diff --git a/liblog/logprint.c b/liblog/logprint.c
index b62f8b4..a2839bf 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -250,6 +250,7 @@
while (!list_empty(&convertHead)) {
struct listnode* node = list_head(&convertHead);
list_remove(node);
+ LOG_ALWAYS_FATAL_IF(node == list_head(&convertHead), "corrupted list");
free(node);
}
}
diff --git a/libmemtrack/Android.bp b/libmemtrack/Android.bp
index 68c580a..0955633 100644
--- a/libmemtrack/Android.bp
+++ b/libmemtrack/Android.bp
@@ -2,6 +2,10 @@
cc_library_shared {
name: "libmemtrack",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: ["memtrack.cpp"],
export_include_dirs: ["include"],
local_include_dirs: ["include"],
diff --git a/libmemunreachable/tests/DisableMalloc_test.cpp b/libmemunreachable/tests/DisableMalloc_test.cpp
index c630049..f446719 100644
--- a/libmemunreachable/tests/DisableMalloc_test.cpp
+++ b/libmemunreachable/tests/DisableMalloc_test.cpp
@@ -73,15 +73,18 @@
TEST_F(DisableMallocTest, deadlock_new) {
ASSERT_DEATH(
{
- char* ptr = new (char);
+ // C++ allows `new Foo` to be replaced with a stack allocation or merged
+ // with future `new Foo` expressions, provided certain conditions are
+ // met [expr.new/10]. None of this applies to `operator new(size_t)`.
+ void* ptr = ::operator new(1);
ASSERT_NE(ptr, nullptr);
- delete (ptr);
+ ::operator delete(ptr);
{
alarm(100ms);
ScopedDisableMalloc disable_malloc;
- char* ptr = new (std::nothrow)(char);
+ void* ptr = ::operator new(1);
ASSERT_NE(ptr, nullptr);
- delete (ptr);
+ ::operator delete(ptr);
}
},
"");
@@ -90,14 +93,12 @@
TEST_F(DisableMallocTest, deadlock_delete) {
ASSERT_DEATH(
{
- char* ptr = new (char);
+ void* ptr = ::operator new(1);
ASSERT_NE(ptr, nullptr);
{
alarm(250ms);
ScopedDisableMalloc disable_malloc;
- delete (ptr);
- // Force ptr usage or this code gets optimized away by the arm64 compiler.
- ASSERT_NE(ptr, nullptr);
+ ::operator delete(ptr);
}
},
"");
diff --git a/libmemunreachable/tests/MemUnreachable_test.cpp b/libmemunreachable/tests/MemUnreachable_test.cpp
index ec89388..87417f1 100644
--- a/libmemunreachable/tests/MemUnreachable_test.cpp
+++ b/libmemunreachable/tests/MemUnreachable_test.cpp
@@ -27,6 +27,9 @@
class HiddenPointer {
public:
+ // Since we're doing such a good job of hiding it, the static analyzer
+ // thinks that we're leaking this `malloc`. This is probably related to
+ // https://bugs.llvm.org/show_bug.cgi?id=34198. NOLINTNEXTLINE
explicit HiddenPointer(size_t size = 256) { Set(malloc(size)); }
~HiddenPointer() { Free(); }
void* Get() { return reinterpret_cast<void*>(~ptr_); }
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index c692d1f..6549b8d 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -18,11 +18,6 @@
"-Wall",
"-Wextra",
"-Werror",
-
- // 524291 corresponds to sysui_histogram, from
- // frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags
- "-DHISTOGRAM_LOG_TAG=524292",
- "-DCOUNT_LOG_TAG=524290",
],
}
diff --git a/libmetricslogger/include/metricslogger/metrics_logger.h b/libmetricslogger/include/metricslogger/metrics_logger.h
index 36e124d..189bc4b 100644
--- a/libmetricslogger/include/metricslogger/metrics_logger.h
+++ b/libmetricslogger/include/metricslogger/metrics_logger.h
@@ -28,14 +28,26 @@
// log buffer.
void LogCounter(const std::string& name, int32_t val);
+// Logs a Tron multi_action with category|category| containing the string
+// |value| in the field |field|.
+void LogMultiAction(int32_t category, int32_t field, const std::string& value);
+
// TODO: replace these with the metric_logger.proto definitions
enum {
LOGBUILDER_CATEGORY = 757,
+ LOGBUILDER_TYPE = 758,
LOGBUILDER_NAME = 799,
LOGBUILDER_BUCKET = 801,
LOGBUILDER_VALUE = 802,
LOGBUILDER_COUNTER = 803,
LOGBUILDER_HISTOGRAM = 804,
+
+ ACTION_BOOT = 1098,
+ FIELD_PLATFORM_REASON = 1099,
+};
+
+enum {
+ TYPE_ACTION = 4,
};
} // namespace metricslogger
diff --git a/libmetricslogger/metrics_logger.cpp b/libmetricslogger/metrics_logger.cpp
index 6f65e10..fdc4407 100644
--- a/libmetricslogger/metrics_logger.cpp
+++ b/libmetricslogger/metrics_logger.cpp
@@ -18,24 +18,40 @@
#include <cstdlib>
+#include <log/event_tag_map.h>
#include <log/log_event_list.h>
+namespace {
+
+EventTagMap* kEventTagMap = android_openEventTagMap(nullptr);
+const int kSysuiMultiActionTag = android_lookupEventTagNum(
+ kEventTagMap, "sysui_multi_action", "(content|4)", ANDROID_LOG_UNKNOWN);
+
+} // namespace
+
namespace android {
namespace metricslogger {
// Mirror com.android.internal.logging.MetricsLogger#histogram().
void LogHistogram(const std::string& event, int32_t data) {
- android_log_event_list log(HISTOGRAM_LOG_TAG);
+ android_log_event_list log(kSysuiMultiActionTag);
log << LOGBUILDER_CATEGORY << LOGBUILDER_HISTOGRAM << LOGBUILDER_NAME << event
<< LOGBUILDER_BUCKET << data << LOGBUILDER_VALUE << 1 << LOG_ID_EVENTS;
}
// Mirror com.android.internal.logging.MetricsLogger#count().
void LogCounter(const std::string& name, int32_t val) {
- android_log_event_list log(COUNT_LOG_TAG);
+ android_log_event_list log(kSysuiMultiActionTag);
log << LOGBUILDER_CATEGORY << LOGBUILDER_COUNTER << LOGBUILDER_NAME << name << LOGBUILDER_VALUE
<< val << LOG_ID_EVENTS;
}
+// Mirror com.android.internal.logging.MetricsLogger#action().
+void LogMultiAction(int32_t category, int32_t field, const std::string& value) {
+ android_log_event_list log(kSysuiMultiActionTag);
+ log << LOGBUILDER_CATEGORY << category << LOGBUILDER_TYPE << TYPE_ACTION
+ << field << value << LOG_ID_EVENTS;
+}
+
} // namespace metricslogger
} // namespace android
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/libnetutils/Android.bp b/libnetutils/Android.bp
index 9967ef8..1d43775 100644
--- a/libnetutils/Android.bp
+++ b/libnetutils/Android.bp
@@ -1,6 +1,9 @@
cc_library_shared {
name: "libnetutils",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"dhcpclient.c",
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
index aedaa38..b568ee5 100644
--- a/libprocinfo/Android.bp
+++ b/libprocinfo/Android.bp
@@ -23,6 +23,9 @@
cc_library {
name: "libprocinfo",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
host_supported: true,
srcs: [
"process.cpp",
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/libsparse/sparse_read.cpp b/libsparse/sparse_read.cpp
index bfb70c7..4379635 100644
--- a/libsparse/sparse_read.cpp
+++ b/libsparse/sparse_read.cpp
@@ -24,6 +24,7 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <string>
#include <unistd.h>
diff --git a/libsuspend/Android.bp b/libsuspend/Android.bp
index 130800e..32f1e1f 100644
--- a/libsuspend/Android.bp
+++ b/libsuspend/Android.bp
@@ -3,6 +3,9 @@
cc_library {
name: "libsuspend",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"autosuspend.c",
diff --git a/libsync/Android.bp b/libsync/Android.bp
index 1646348..3fae5e6 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -22,10 +22,15 @@
cc_library_shared {
name: "libsync",
- vendor_available: true,
defaults: ["libsync_defaults"],
}
+llndk_library {
+ name: "libsync",
+ symbol_file: "libsync.map.txt",
+ export_include_dirs: ["include"],
+}
+
// libsync_recovery is only intended for the recovery binary.
// Future versions of the kernel WILL require an updated libsync, and will break
// anything statically linked against the current libsync.
diff --git a/libsync/libsync.map.txt b/libsync/libsync.map.txt
index daa28ae..53bb07a 100644
--- a/libsync/libsync.map.txt
+++ b/libsync/libsync.map.txt
@@ -17,16 +17,12 @@
LIBSYNC {
global:
sync_merge; # introduced=26
- sync_get_fence_info; # introduced=26
- sync_free_fence_info; # introduced=26
+ sync_file_info; # introduced=26
+ sync_file_info_free; # introduced=26
+ sync_wait; # vndk
+ sync_fence_info; # vndk
+ sync_pt_info; # vndk
+ sync_fence_info_free; # vndk
local:
*;
};
-
-LIBSYNC_PLATFORM {
- global:
- sync_wait;
- sync_fence_info;
- sync_pt_info;
- sync_fence_info_free;
-} LIBSYNC_PLATFORM;
diff --git a/libsync/sync.c b/libsync/sync.c
index baeccda..6b187fa 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -217,6 +217,8 @@
local_info.num_fences * sizeof(struct sync_fence_info));
if (!info)
return NULL;
+
+ info->num_fences = local_info.num_fences;
info->sync_fence_info = (__u64)(uintptr_t)(info + 1);
err = ioctl(fd, SYNC_IOC_FILE_INFO, info);
@@ -275,7 +277,6 @@
info = calloc(1, sizeof(struct sync_file_info) +
num_fences * sizeof(struct sync_fence_info));
if (!info) {
- free(legacy_info);
return NULL;
}
info->sync_fence_info = (__u64)(uintptr_t)(info + 1);
diff --git a/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp
index f08e97e..0fb86d6 100644
--- a/libsync/tests/sync_test.cpp
+++ b/libsync/tests/sync_test.cpp
@@ -448,6 +448,41 @@
ASSERT_EQ(mergedFence.wait(100), 0);
}
+TEST(FenceTest, GetInfoActive) {
+ SyncTimeline timeline;
+ ASSERT_TRUE(timeline.isValid());
+
+ SyncFence fence(timeline, 1);
+ ASSERT_TRUE(fence.isValid());
+
+ vector<SyncPointInfo> info = fence.getInfo();
+ ASSERT_EQ(info.size(), 1);
+
+ ASSERT_FALSE(info[0].driverName.empty());
+ ASSERT_FALSE(info[0].objectName.empty());
+ ASSERT_EQ(info[0].timeStampNs, 0);
+ ASSERT_EQ(info[0].status, 0);
+}
+
+TEST(FenceTest, GetInfoSignaled) {
+ SyncTimeline timeline;
+ ASSERT_TRUE(timeline.isValid());
+
+ SyncFence fence(timeline, 1);
+ ASSERT_TRUE(fence.isValid());
+
+ ASSERT_EQ(timeline.inc(1), 0);
+ ASSERT_EQ(fence.wait(), 0);
+
+ vector<SyncPointInfo> info = fence.getInfo();
+ ASSERT_EQ(info.size(), 1);
+
+ ASSERT_FALSE(info[0].driverName.empty());
+ ASSERT_FALSE(info[0].objectName.empty());
+ ASSERT_GT(info[0].timeStampNs, 0);
+ ASSERT_EQ(info[0].status, 1);
+}
+
TEST(StressTest, TwoThreadsSharedTimeline) {
const int iterations = 1 << 16;
int counter = 0;
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 550ef42..3a12292 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -1,6 +1,9 @@
cc_library_shared {
name: "libsysutils",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
srcs: [
"src/SocketListener.cpp",
@@ -20,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 b971a9e..f40086e 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -29,12 +29,19 @@
darwin: {
enabled: false,
},
+ linux_bionic: {
+ enabled: true,
+ },
},
}
cc_library {
name: "libunwindstack",
vendor_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
defaults: ["libunwindstack_flags"],
export_include_dirs: ["include"],
@@ -54,9 +61,17 @@
"Maps.cpp",
"Memory.cpp",
"Regs.cpp",
+ "Unwinder.cpp",
"Symbols.cpp",
],
+ target: {
+ // Always disable optimizations for host to make it easier to debug.
+ host: {
+ cflags: ["-O0", "-g"],
+ },
+ },
+
arch: {
x86: {
srcs: ["AsmGetRegsX86.S"],
@@ -92,12 +107,12 @@
"tests/DwarfOpTest.cpp",
"tests/DwarfSectionTest.cpp",
"tests/DwarfSectionImplTest.cpp",
+ "tests/ElfFake.cpp",
"tests/ElfInterfaceArmTest.cpp",
"tests/ElfInterfaceTest.cpp",
"tests/ElfTest.cpp",
"tests/ElfTestUtils.cpp",
"tests/LogFake.cpp",
- "tests/MapInfoCreateMemoryTest.cpp",
"tests/MapInfoGetElfTest.cpp",
"tests/MapsTest.cpp",
"tests/MemoryBufferTest.cpp",
@@ -111,6 +126,7 @@
"tests/RegsTest.cpp",
"tests/SymbolsTest.cpp",
"tests/UnwindTest.cpp",
+ "tests/UnwinderTest.cpp",
],
cflags: [
@@ -129,14 +145,6 @@
"libgmock",
],
- target: {
- linux: {
- host_ldlibs: [
- "-lrt",
- ],
- },
- },
-
data: [
"tests/files/elf32.xz",
"tests/files/elf64.xz",
@@ -164,14 +172,6 @@
srcs: [
"tools/unwind.cpp",
],
-
- target: {
- linux: {
- host_ldlibs: [
- "-lrt",
- ],
- },
- },
}
cc_binary {
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 1234eb1..8b30b76 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -47,7 +47,7 @@
return nullptr;
}
-bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
+bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
last_error_ = DWARF_ERROR_NONE;
const DwarfFde* fde = GetFdeFromPc(pc);
if (fde == nullptr || fde->cie == nullptr) {
@@ -62,7 +62,7 @@
}
// Now eval the actual registers.
- return Eval(fde->cie, process_memory, loc_regs, regs);
+ return Eval(fde->cie, process_memory, loc_regs, regs, finished);
}
template <typename AddressType>
@@ -92,7 +92,8 @@
template <typename AddressType>
bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_memory,
- const dwarf_loc_regs_t& loc_regs, Regs* regs) {
+ const dwarf_loc_regs_t& loc_regs, Regs* regs,
+ bool* finished) {
RegsImpl<AddressType>* cur_regs = reinterpret_cast<RegsImpl<AddressType>*>(regs);
if (cie->return_address_register >= cur_regs->total_regs()) {
last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
@@ -224,12 +225,14 @@
// Find the return address location.
if (return_address_undefined) {
cur_regs->set_pc(0);
+ *finished = true;
} else {
cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
+ *finished = false;
}
cur_regs->set_sp(cfa);
- // Stop if the cfa and pc are the same.
- return prev_cfa != cfa || prev_pc != cur_regs->pc();
+ // Return false if the unwind is not finished or the cfa and pc didn't change.
+ return *finished || prev_cfa != cfa || prev_pc != cur_regs->pc();
}
template <typename AddressType>
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 4fc7c67..dc6591d 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -95,11 +95,17 @@
gnu_debugdata_interface_->GetFunctionName(addr, name, func_offset)));
}
-bool Elf::Step(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
- return valid_ && (regs->StepIfSignalHandler(rel_pc, this, process_memory) ||
- interface_->Step(rel_pc, regs, process_memory) ||
- (gnu_debugdata_interface_ &&
- gnu_debugdata_interface_->Step(rel_pc, regs, process_memory)));
+bool Elf::Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished) {
+ if (!valid_) {
+ return false;
+ }
+ if (regs->StepIfSignalHandler(rel_pc, this, process_memory)) {
+ *finished = false;
+ return true;
+ }
+ return interface_->Step(rel_pc, regs, process_memory, finished) ||
+ (gnu_debugdata_interface_ &&
+ gnu_debugdata_interface_->Step(rel_pc, regs, process_memory, finished));
}
uint64_t Elf::GetLoadBias() {
@@ -124,6 +130,28 @@
return true;
}
+void Elf::GetInfo(Memory* memory, bool* valid, uint64_t* size) {
+ if (!IsValidElf(memory)) {
+ *valid = false;
+ return;
+ }
+ *size = 0;
+ *valid = true;
+
+ // Now read the section header information.
+ uint8_t class_type;
+ if (!memory->Read(EI_CLASS, &class_type, 1)) {
+ return;
+ }
+ if (class_type == ELFCLASS32) {
+ ElfInterface32::GetMaxSize(memory, size);
+ } else if (class_type == ELFCLASS64) {
+ ElfInterface64::GetMaxSize(memory, size);
+ } else {
+ *valid = false;
+ }
+}
+
ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) {
if (!IsValidElf(memory)) {
return nullptr;
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 75abc85..46a3f3f 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -348,7 +348,7 @@
return false;
}
-bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
+bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
// Need to subtract off the load_bias to get the correct pc.
if (pc < load_bias_) {
return false;
@@ -357,19 +357,34 @@
// Try the eh_frame first.
DwarfSection* eh_frame = eh_frame_.get();
- if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory)) {
+ if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory, finished)) {
return true;
}
// Try the debug_frame next.
DwarfSection* debug_frame = debug_frame_.get();
- if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory)) {
+ if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory, finished)) {
return true;
}
-
return false;
}
+// This is an estimation of the size of the elf file using the location
+// of the section headers and size. This assumes that the section headers
+// are at the end of the elf file. If the elf has a load bias, the size
+// will be too large, but this is acceptable.
+template <typename EhdrType>
+void ElfInterface::GetMaxSizeWithTemplate(Memory* memory, uint64_t* size) {
+ EhdrType ehdr;
+ if (!memory->Read(0, &ehdr, sizeof(ehdr))) {
+ return;
+ }
+ if (ehdr.e_shnum == 0) {
+ return;
+ }
+ *size = ehdr.e_shoff + ehdr.e_shentsize * ehdr.e_shnum;
+}
+
// Instantiate all of the needed template functions.
template void ElfInterface::InitHeadersWithTemplate<uint32_t>();
template void ElfInterface::InitHeadersWithTemplate<uint64_t>();
@@ -391,4 +406,7 @@
template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, std::string*,
uint64_t*);
+template void ElfInterface::GetMaxSizeWithTemplate<Elf32_Ehdr>(Memory*, uint64_t*);
+template void ElfInterface::GetMaxSizeWithTemplate<Elf64_Ehdr>(Memory*, uint64_t*);
+
} // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index 66bc51f..17364d0 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -99,22 +99,25 @@
return true;
}
-bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
+bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
// Dwarf unwind information is precise about whether a pc is covered or not,
// but arm unwind information only has ranges of pc. In order to avoid
// incorrectly doing a bad unwind using arm unwind information for a
// different function, always try and unwind with the dwarf information first.
- return ElfInterface32::Step(pc, regs, process_memory) || StepExidx(pc, regs, process_memory);
+ return ElfInterface32::Step(pc, regs, process_memory, finished) ||
+ StepExidx(pc, regs, process_memory, finished);
}
-bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory) {
+bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
uint64_t entry_offset;
if (!FindEntry(pc, &entry_offset)) {
return false;
}
+
ArmExidx arm(regs_arm, memory_, process_memory);
arm.set_cfa(regs_arm->sp());
+ bool return_value = false;
if (arm.ExtractEntryData(entry_offset) && arm.Eval()) {
// If the pc was not set, then use the LR registers for the PC.
if (!arm.pc_set()) {
@@ -125,9 +128,15 @@
}
regs_arm->set_sp(arm.cfa());
(*regs_arm)[ARM_REG_SP] = regs_arm->sp();
+ *finished = false;
+ return_value = true;
+ }
+
+ if (arm.status() == ARM_STATUS_NO_UNWIND) {
+ *finished = true;
return true;
}
- return false;
+ return return_value;
}
} // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index 1f4e8cb..bfe7704 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -70,9 +70,9 @@
bool HandleType(uint64_t offset, uint32_t type) override;
- bool Step(uint64_t pc, Regs* regs, Memory* process_memory) override;
+ bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) override;
- bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory);
+ bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);
uint64_t start_offset() { return start_offset_; }
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index d0e1216..96f2cb4 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
@@ -27,60 +28,88 @@
namespace unwindstack {
-Memory* MapInfo::CreateMemory(pid_t pid) {
+Memory* MapInfo::GetFileMemory() {
+ std::unique_ptr<MemoryFileAtOffset> memory(new MemoryFileAtOffset);
+ if (offset == 0) {
+ if (memory->Init(name, 0)) {
+ return memory.release();
+ }
+ return nullptr;
+ }
+
+ // There are two possibilities when the offset is non-zero.
+ // - There is an elf file embedded in a file.
+ // - The whole file is an elf file, and the offset needs to be saved.
+ //
+ // Map in just the part of the file for the map. If this is not
+ // a valid elf, then reinit as if the whole file is an elf file.
+ // If the offset is a valid elf, then determine the size of the map
+ // and reinit to that size. This is needed because the dynamic linker
+ // only maps in a portion of the original elf, and never the symbol
+ // file data.
+ uint64_t map_size = end - start;
+ if (!memory->Init(name, offset, map_size)) {
+ return nullptr;
+ }
+
+ bool valid;
+ uint64_t max_size;
+ Elf::GetInfo(memory.get(), &valid, &max_size);
+ if (!valid) {
+ // Init as if the whole file is an elf.
+ if (memory->Init(name, 0)) {
+ elf_offset = offset;
+ return memory.release();
+ }
+ return nullptr;
+ }
+
+ if (max_size > map_size) {
+ if (memory->Init(name, offset, max_size)) {
+ return memory.release();
+ }
+ // Try to reinit using the default map_size.
+ if (memory->Init(name, offset, map_size)) {
+ return memory.release();
+ }
+ return nullptr;
+ }
+ return memory.release();
+}
+
+Memory* MapInfo::CreateMemory(const std::shared_ptr<Memory>& process_memory) {
if (end <= start) {
return nullptr;
}
elf_offset = 0;
+ // Fail on device maps.
+ if (flags & MAPS_FLAGS_DEVICE_MAP) {
+ return nullptr;
+ }
+
// First try and use the file associated with the info.
if (!name.empty()) {
- // Fail on device maps.
- if (flags & MAPS_FLAGS_DEVICE_MAP) {
- return nullptr;
- }
-
- std::unique_ptr<MemoryFileAtOffset> file_memory(new MemoryFileAtOffset);
- uint64_t map_size;
- if (offset != 0) {
- // Only map in a piece of the file.
- map_size = end - start;
- } else {
- map_size = UINT64_MAX;
- }
- if (file_memory->Init(name, offset, map_size)) {
- // It's possible that a non-zero offset might not be pointing to
- // valid elf data. Check if this is a valid elf, and if not assume
- // that this was meant to incorporate the entire file.
- if (offset != 0 && !Elf::IsValidElf(file_memory.get())) {
- // Don't bother checking the validity that will happen on the elf init.
- if (file_memory->Init(name, 0)) {
- elf_offset = offset;
- return file_memory.release();
- }
- // Fall through if the init fails.
- } else {
- return file_memory.release();
- }
+ Memory* memory = GetFileMemory();
+ if (memory != nullptr) {
+ return memory;
}
}
- Memory* memory = nullptr;
- if (pid == getpid()) {
- memory = new MemoryLocal();
- } else {
- memory = new MemoryRemote(pid);
+ // If the map isn't readable, don't bother trying to read from process memory.
+ if (!(flags & PROT_READ)) {
+ return nullptr;
}
- return new MemoryRange(memory, start, end);
+ return new MemoryRange(process_memory, start, end);
}
-Elf* MapInfo::GetElf(pid_t pid, bool init_gnu_debugdata) {
+Elf* MapInfo::GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata) {
if (elf) {
return elf;
}
- elf = new Elf(CreateMemory(pid));
+ elf = new Elf(CreateMemory(process_memory));
if (elf->Init() && init_gnu_debugdata) {
elf->InitGnuDebugdata();
}
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 8c36055..32753df 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -52,6 +52,13 @@
return false;
}
+std::shared_ptr<Memory> Memory::CreateProcessMemory(pid_t pid) {
+ if (pid == getpid()) {
+ return std::shared_ptr<Memory>(new MemoryLocal());
+ }
+ return std::shared_ptr<Memory>(new MemoryRemote(pid));
+}
+
bool MemoryBuffer::Read(uint64_t addr, void* dst, size_t size) {
uint64_t last_read_byte;
if (__builtin_add_overflow(size, addr, &last_read_byte)) {
@@ -249,7 +256,7 @@
return true;
}
-MemoryRange::MemoryRange(Memory* memory, uint64_t begin, uint64_t end)
+MemoryRange::MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t end)
: memory_(memory), begin_(begin), length_(end - begin) {
CHECK(end > begin);
}
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index 4d09c1b..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)) {}
@@ -91,6 +71,15 @@
set_sp(regs_[ARM_REG_SP]);
}
+bool RegsArm::SetPcFromReturnAddress(Memory*) {
+ if (pc() == regs_[ARM_REG_LR]) {
+ return false;
+ }
+
+ set_pc(regs_[ARM_REG_LR]);
+ return true;
+}
+
RegsArm64::RegsArm64()
: RegsImpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
@@ -114,6 +103,15 @@
set_sp(regs_[ARM64_REG_SP]);
}
+bool RegsArm64::SetPcFromReturnAddress(Memory*) {
+ if (pc() == regs_[ARM64_REG_LR]) {
+ return false;
+ }
+
+ set_pc(regs_[ARM64_REG_LR]);
+ return true;
+}
+
RegsX86::RegsX86()
: RegsImpl<uint32_t>(X86_REG_LAST, X86_REG_SP, Location(LOCATION_SP_OFFSET, -4)) {}
@@ -137,6 +135,17 @@
set_sp(regs_[X86_REG_SP]);
}
+bool RegsX86::SetPcFromReturnAddress(Memory* process_memory) {
+ // Attempt to get the return address from the top of the stack.
+ uint32_t new_pc;
+ if (!process_memory->Read(sp_, &new_pc, sizeof(new_pc)) || new_pc == pc()) {
+ return false;
+ }
+
+ set_pc(new_pc);
+ return true;
+}
+
RegsX86_64::RegsX86_64()
: RegsImpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP, Location(LOCATION_SP_OFFSET, -8)) {}
@@ -161,6 +170,17 @@
set_sp(regs_[X86_64_REG_SP]);
}
+bool RegsX86_64::SetPcFromReturnAddress(Memory* process_memory) {
+ // Attempt to get the return address from the top of the stack.
+ uint64_t new_pc;
+ if (!process_memory->Read(sp_, &new_pc, sizeof(new_pc)) || new_pc == pc()) {
+ return false;
+ }
+
+ set_pc(new_pc);
+ return true;
+}
+
static Regs* ReadArm(void* remote_data) {
arm_user_regs* user = reinterpret_cast<arm_user_regs*>(remote_data);
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
new file mode 100644
index 0000000..e648927
--- /dev/null
+++ b/libunwindstack/Unwinder.cpp
@@ -0,0 +1,183 @@
+/*
+ * 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.
+ */
+
+#define _GNU_SOURCE 1
+#include <elf.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/stringprintf.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Unwinder.h>
+
+namespace unwindstack {
+
+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 = rel_pc;
+
+ if (map_info == nullptr) {
+ return;
+ }
+
+ if (adjust_pc) {
+ // Don't adjust the first frame pc.
+ frame->rel_pc = regs_->GetAdjustedPc(rel_pc, elf);
+
+ // Adjust the original 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 = "";
+ frame->function_offset = 0;
+ }
+}
+
+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;
+ 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 {
+ 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) {
+ if (return_address_attempt) {
+ // 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())) {
+ break;
+ }
+ return_address_attempt = true;
+ }
+ } else {
+ return_address_attempt = false;
+ }
+ }
+}
+
+std::string Unwinder::FormatFrame(size_t frame_num) {
+ if (frame_num >= frames_.size()) {
+ return "";
+ }
+ return FormatFrame(frames_[frame_num],
+ regs_->MachineType() == EM_ARM || regs_->MachineType() == EM_386);
+}
+
+std::string Unwinder::FormatFrame(const FrameData& frame, bool bits32) {
+ std::string data;
+
+ if (bits32) {
+ data += android::base::StringPrintf(" #%02zu pc %08" PRIx64, frame.num, frame.rel_pc);
+ } else {
+ data += android::base::StringPrintf(" #%02zu pc %016" PRIx64, frame.num, frame.rel_pc);
+ }
+
+ if (frame.map_offset != 0) {
+ data += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", frame.map_offset);
+ }
+
+ if (frame.map_start == frame.map_end) {
+ // No valid map associated with this frame.
+ data += " <unknown>";
+ } else if (!frame.map_name.empty()) {
+ data += " " + frame.map_name;
+ } else {
+ data += android::base::StringPrintf(" <anonymous:%" PRIx64 ">", frame.map_start);
+ }
+ if (!frame.function_name.empty()) {
+ data += " (" + frame.function_name;
+ if (frame.function_offset != 0) {
+ data += android::base::StringPrintf("+%" PRId64, frame.function_offset);
+ }
+ data += ')';
+ }
+ return data;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index 26485ae..1e843c3 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -76,7 +76,7 @@
virtual bool Init(uint64_t offset, uint64_t size) = 0;
- virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*) = 0;
+ virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*) = 0;
virtual bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) = 0;
@@ -100,7 +100,7 @@
virtual uint64_t AdjustPcFromFde(uint64_t pc) = 0;
- bool Step(uint64_t pc, Regs* regs, Memory* process_memory);
+ bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);
protected:
DwarfMemory memory_;
@@ -119,7 +119,7 @@
virtual ~DwarfSectionImpl() = default;
bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
- Regs* regs) override;
+ Regs* regs, bool* finished) override;
const DwarfCie* GetCie(uint64_t offset);
bool FillInCie(DwarfCie* cie);
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index d89a746..f246beb 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -50,7 +50,7 @@
uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
- bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory);
+ bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
ElfInterface* CreateInterfaceFromMemory(Memory* memory);
@@ -70,6 +70,8 @@
static bool IsValidElf(Memory* memory);
+ static void GetInfo(Memory* memory, bool* valid, uint64_t* size);
+
protected:
bool valid_ = false;
std::unique_ptr<ElfInterface> interface_;
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 5cac0d3..4fe966f 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -59,7 +59,7 @@
virtual bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) = 0;
- virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory);
+ virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
Memory* CreateGnuDebugdataMemory();
@@ -102,6 +102,9 @@
virtual bool HandleType(uint64_t, uint32_t) { return false; }
+ template <typename EhdrType>
+ static void GetMaxSizeWithTemplate(Memory* memory, uint64_t* size);
+
Memory* memory_;
std::unordered_map<uint64_t, LoadInfo> pt_loads_;
uint64_t load_bias_ = 0;
@@ -146,6 +149,10 @@
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, name, func_offset);
}
+
+ static void GetMaxSize(Memory* memory, uint64_t* size) {
+ GetMaxSizeWithTemplate<Elf32_Ehdr>(memory, size);
+ }
};
class ElfInterface64 : public ElfInterface {
@@ -166,6 +173,10 @@
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset) override {
return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, name, func_offset);
}
+
+ static void GetMaxSize(Memory* memory, uint64_t* size) {
+ GetMaxSizeWithTemplate<Elf64_Ehdr>(memory, size);
+ }
};
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/MapInfo.h b/libunwindstack/include/unwindstack/MapInfo.h
index 1854767..f108766 100644
--- a/libunwindstack/include/unwindstack/MapInfo.h
+++ b/libunwindstack/include/unwindstack/MapInfo.h
@@ -40,9 +40,13 @@
// instead of a portion of the file.
uint64_t elf_offset;
- Memory* CreateMemory(pid_t pid);
// This function guarantees it will never return nullptr.
- Elf* GetElf(pid_t pid, bool init_gnu_debugdata = false);
+ Elf* GetElf(const std::shared_ptr<Memory>& process_memory, bool init_gnu_debugdata = false);
+
+ private:
+ Memory* GetFileMemory();
+
+ Memory* CreateMemory(const std::shared_ptr<Memory>& process_memory);
};
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index 0c05266..183b899 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -21,6 +21,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include <memory>
#include <string>
#include <vector>
@@ -31,6 +32,8 @@
Memory() = default;
virtual ~Memory() = default;
+ static std::shared_ptr<Memory> CreateProcessMemory(pid_t pid);
+
virtual bool ReadString(uint64_t addr, std::string* string, uint64_t max_read = UINT64_MAX);
virtual bool Read(uint64_t addr, void* dst, size_t size) = 0;
@@ -125,13 +128,13 @@
class MemoryRange : public Memory {
public:
- MemoryRange(Memory* memory, uint64_t begin, uint64_t end);
- virtual ~MemoryRange() { delete memory_; }
+ MemoryRange(const std::shared_ptr<Memory>& memory, uint64_t begin, uint64_t end);
+ virtual ~MemoryRange() = default;
bool Read(uint64_t addr, void* dst, size_t size) override;
private:
- Memory* memory_;
+ std::shared_ptr<Memory> memory_;
uint64_t begin_;
uint64_t length_;
};
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index ed4d38a..9d3150b 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -55,14 +55,14 @@
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;
virtual void SetFromRaw() = 0;
+ virtual bool SetPcFromReturnAddress(Memory* process_memory) = 0;
+
uint16_t sp_reg() { return sp_reg_; }
uint16_t total_regs() { return total_regs_; }
@@ -84,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_; }
@@ -113,6 +111,8 @@
void SetFromRaw() override;
+ bool SetPcFromReturnAddress(Memory* process_memory) override;
+
bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
};
@@ -127,6 +127,8 @@
void SetFromRaw() override;
+ bool SetPcFromReturnAddress(Memory* process_memory) override;
+
bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
};
@@ -141,6 +143,8 @@
void SetFromRaw() override;
+ bool SetPcFromReturnAddress(Memory* process_memory) override;
+
bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
void SetFromUcontext(x86_ucontext_t* ucontext);
@@ -157,6 +161,8 @@
void SetFromRaw() override;
+ bool SetPcFromReturnAddress(Memory* process_memory) override;
+
bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
void SetFromUcontext(x86_64_ucontext_t* ucontext);
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
new file mode 100644
index 0000000..71703b4
--- /dev/null
+++ b/libunwindstack/include/unwindstack/Unwinder.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_UNWINDER_H
+#define _LIBUNWINDSTACK_UNWINDER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Elf;
+
+struct FrameData {
+ size_t num;
+
+ uint64_t rel_pc;
+ uint64_t pc;
+ uint64_t sp;
+
+ std::string function_name;
+ uint64_t function_offset;
+
+ std::string map_name;
+ uint64_t map_offset;
+ uint64_t map_start;
+ uint64_t map_end;
+ uint64_t map_load_bias;
+ int map_flags;
+};
+
+class Unwinder {
+ public:
+ Unwinder(size_t max_frames, Maps* maps, Regs* regs, std::shared_ptr<Memory> process_memory)
+ : max_frames_(max_frames), maps_(maps), regs_(regs), process_memory_(process_memory) {
+ frames_.reserve(max_frames);
+ }
+ ~Unwinder() = default;
+
+ void Unwind(std::set<std::string>* initial_map_names_to_skip = nullptr);
+
+ size_t NumFrames() { return frames_.size(); }
+
+ const std::vector<FrameData>& frames() { return frames_; }
+
+ std::string FormatFrame(size_t frame_num);
+ static std::string FormatFrame(const FrameData& frame, bool bits32);
+
+ private:
+ void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t rel_pc, bool adjust_pc);
+
+ size_t max_frames_;
+ Maps* maps_;
+ Regs* regs_;
+ std::vector<FrameData> frames_;
+ std::shared_ptr<Memory> process_memory_;
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_UNWINDER_H
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 b871539..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);
@@ -98,13 +98,14 @@
regs[5] = 0x20;
regs[9] = 0x3000;
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
- ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ bool finished;
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
}
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);
@@ -113,13 +114,14 @@
regs[9] = 0x3000;
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x96, 0x96, 0x96});
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
- ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ bool finished;
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
}
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);
@@ -130,14 +132,16 @@
TypeParam cfa_value = 0x12345;
this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
- ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ EXPECT_FALSE(finished);
EXPECT_EQ(0x12345U, regs.sp());
EXPECT_EQ(0x20U, regs.pc());
}
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);
@@ -146,14 +150,16 @@
regs[9] = 0x3000;
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
- ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ ASSERT_FALSE(finished);
EXPECT_EQ(0x80000000U, regs.sp());
EXPECT_EQ(0x20U, regs.pc());
}
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);
@@ -162,59 +168,63 @@
regs[9] = 0x3000;
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x50, 0x96, 0x96});
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
- ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ bool finished;
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->section_->last_error());
}
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;
- ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ bool finished;
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
}
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;
- ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ bool finished;
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(DWARF_ERROR_CFA_NOT_DEFINED, this->section_->last_error());
}
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}};
- ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ bool finished;
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
this->section_->TestClearError();
loc_regs.erase(CFA_REG);
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_INVALID, {0, 0}};
- ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
this->section_->TestClearError();
loc_regs.erase(CFA_REG);
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_OFFSET, {0, 0}};
- ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
this->section_->TestClearError();
loc_regs.erase(CFA_REG);
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0, 0}};
- ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
}
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);
@@ -222,14 +232,16 @@
regs[5] = 0x20;
regs[9] = 0x3000;
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {9, 0}};
- ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ EXPECT_FALSE(finished);
EXPECT_EQ(0x20U, regs.pc());
EXPECT_EQ(0x2000U, regs.sp());
}
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);
@@ -238,14 +250,16 @@
regs[6] = 0x4000;
regs[9] = 0x3000;
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {6, 0}};
- ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ EXPECT_FALSE(finished);
EXPECT_EQ(0x20U, regs.pc());
EXPECT_EQ(0x4000U, regs.sp());
}
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);
@@ -254,13 +268,14 @@
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {3, 0}};
loc_regs[9] = DwarfLocation{DWARF_LOCATION_REGISTER, {1, 0}};
- ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ bool finished;
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
}
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);
@@ -268,13 +283,14 @@
regs[8] = 0x10;
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {10, 0}};
- ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ bool finished;
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
}
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)) {
@@ -292,7 +308,9 @@
loc_regs[1] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0x100, 0}};
loc_regs[2] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x50, 0}};
loc_regs[3] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
- ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ EXPECT_FALSE(finished);
EXPECT_EQ(0x10U, regs.pc());
EXPECT_EQ(0x2100U, regs.sp());
EXPECT_EQ(0x2200U, regs[1]);
@@ -306,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);
@@ -315,14 +333,16 @@
regs[8] = 0x10;
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
loc_regs[5] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
- ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ EXPECT_TRUE(finished);
EXPECT_EQ(0U, regs.pc());
EXPECT_EQ(0x10U, regs.sp());
}
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);
@@ -330,14 +350,16 @@
regs[5] = 0x20;
regs[8] = 0x10;
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
- ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ EXPECT_FALSE(finished);
EXPECT_EQ(0x20U, regs.pc());
EXPECT_EQ(0x10U, regs.sp());
}
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);
@@ -347,14 +369,16 @@
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
// This should not result in any errors.
loc_regs[20] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
- ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ EXPECT_FALSE(finished);
EXPECT_EQ(0x20U, regs.pc());
EXPECT_EQ(0x10U, regs.sp());
}
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);
@@ -365,14 +389,16 @@
this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
loc_regs[5] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
- ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ EXPECT_FALSE(finished);
EXPECT_EQ(0x3000U, regs.sp());
EXPECT_EQ(0x12345U, regs.pc());
}
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);
@@ -381,14 +407,16 @@
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
loc_regs[5] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
- ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ EXPECT_FALSE(finished);
EXPECT_EQ(0x3000U, regs.sp());
EXPECT_EQ(0x80000000U, regs.pc());
}
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);
@@ -396,7 +424,8 @@
regs[5] = 0x100;
regs[8] = 0x2000;
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
- ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
+ bool finished;
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(0x2000U, regs.sp());
EXPECT_EQ(0x100U, regs.pc());
}
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index fc67063..3fcd2b6 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -32,7 +32,7 @@
MOCK_METHOD4(Log, bool(uint8_t, uint64_t, uint64_t, const DwarfFde*));
- MOCK_METHOD4(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*));
+ MOCK_METHOD5(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*));
MOCK_METHOD3(GetCfaLocationInfo, bool(uint64_t, const DwarfFde*, dwarf_loc_regs_t*));
@@ -104,7 +104,8 @@
EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
.WillOnce(::testing::Return(false));
- ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+ bool finished;
+ ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr, &finished));
}
TEST_F(DwarfSectionTest, Step_fail_cie_null) {
@@ -118,7 +119,8 @@
.WillOnce(::testing::Return(true));
EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
- ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+ bool finished;
+ ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr, &finished));
}
TEST_F(DwarfSectionTest, Step_fail_cfa_location) {
@@ -136,7 +138,8 @@
EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
.WillOnce(::testing::Return(false));
- ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
+ bool finished;
+ ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr, &finished));
}
TEST_F(DwarfSectionTest, Step_pass) {
@@ -155,10 +158,11 @@
.WillOnce(::testing::Return(true));
MemoryFake process;
- EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr))
+ EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
.WillOnce(::testing::Return(true));
- ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process));
+ bool finished;
+ ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
}
} // namespace unwindstack
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/ElfInterfaceArmTest.cpp b/libunwindstack/tests/ElfInterfaceArmTest.cpp
index c7ef4a1..4df7e1c 100644
--- a/libunwindstack/tests/ElfInterfaceArmTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceArmTest.cpp
@@ -322,7 +322,8 @@
ElfInterfaceArm interface(&memory_);
// FindEntry fails.
- ASSERT_FALSE(interface.StepExidx(0x7000, nullptr, nullptr));
+ bool finished;
+ ASSERT_FALSE(interface.StepExidx(0x7000, nullptr, nullptr, &finished));
// ExtractEntry should fail.
interface.set_start_offset(0x1000);
@@ -335,15 +336,16 @@
regs[ARM_REG_LR] = 0x20000;
regs.set_sp(regs[ARM_REG_SP]);
regs.set_pc(0x1234);
- ASSERT_FALSE(interface.StepExidx(0x7000, ®s, &process_memory_));
+ ASSERT_FALSE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
// Eval should fail.
memory_.SetData32(0x1004, 0x81000000);
- ASSERT_FALSE(interface.StepExidx(0x7000, ®s, &process_memory_));
+ ASSERT_FALSE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
// Everything should pass.
memory_.SetData32(0x1004, 0x80b0b0b0);
- ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_));
+ ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
+ ASSERT_FALSE(finished);
ASSERT_EQ(0x1000U, regs.sp());
ASSERT_EQ(0x1000U, regs[ARM_REG_SP]);
ASSERT_EQ(0x20000U, regs.pc());
@@ -367,11 +369,57 @@
regs.set_pc(0x1234);
// Everything should pass.
- ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_));
+ bool finished;
+ ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
+ ASSERT_FALSE(finished);
ASSERT_EQ(0x10004U, regs.sp());
ASSERT_EQ(0x10004U, regs[ARM_REG_SP]);
ASSERT_EQ(0x10U, regs.pc());
ASSERT_EQ(0x10U, regs[ARM_REG_PC]);
}
+TEST_F(ElfInterfaceArmTest, StepExidx_cant_unwind) {
+ ElfInterfaceArm interface(&memory_);
+
+ interface.set_start_offset(0x1000);
+ interface.set_total_entries(1);
+ memory_.SetData32(0x1000, 0x6000);
+ memory_.SetData32(0x1004, 1);
+
+ RegsArm regs;
+ regs[ARM_REG_SP] = 0x10000;
+ regs[ARM_REG_LR] = 0x20000;
+ regs.set_sp(regs[ARM_REG_SP]);
+ regs.set_pc(0x1234);
+
+ bool finished;
+ ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
+ ASSERT_TRUE(finished);
+ ASSERT_EQ(0x10000U, regs.sp());
+ ASSERT_EQ(0x10000U, regs[ARM_REG_SP]);
+ ASSERT_EQ(0x1234U, regs.pc());
+}
+
+TEST_F(ElfInterfaceArmTest, StepExidx_refuse_unwind) {
+ ElfInterfaceArm interface(&memory_);
+
+ interface.set_start_offset(0x1000);
+ interface.set_total_entries(1);
+ memory_.SetData32(0x1000, 0x6000);
+ memory_.SetData32(0x1004, 0x808000b0);
+
+ RegsArm regs;
+ regs[ARM_REG_SP] = 0x10000;
+ regs[ARM_REG_LR] = 0x20000;
+ regs.set_sp(regs[ARM_REG_SP]);
+ regs.set_pc(0x1234);
+
+ bool finished;
+ ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
+ ASSERT_TRUE(finished);
+ ASSERT_EQ(0x10000U, regs.sp());
+ ASSERT_EQ(0x10000U, regs[ARM_REG_SP]);
+ ASSERT_EQ(0x1234U, regs.pc());
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index ed1be3b..42a0246 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -129,7 +129,8 @@
uint64_t func_offset;
ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
- ASSERT_FALSE(elf.Step(0, nullptr, nullptr));
+ bool finished;
+ ASSERT_FALSE(elf.Step(0, nullptr, nullptr, &finished));
}
TEST_F(ElfTest, elf32_invalid_machine) {
diff --git a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 9e45e78..d2aad49 100644
--- a/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -34,49 +34,78 @@
#include <unwindstack/MapInfo.h>
#include <unwindstack/Memory.h>
+#include "MemoryFake.h"
+
namespace unwindstack {
class MapInfoCreateMemoryTest : public ::testing::Test {
protected:
+ template <typename Ehdr, typename Shdr>
+ static void InitElf(int fd, uint64_t file_offset, uint64_t sh_offset, uint8_t class_type) {
+ std::vector<uint8_t> buffer(20000);
+ memset(buffer.data(), 0, buffer.size());
+
+ Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
+ ehdr.e_ident[EI_CLASS] = class_type;
+ ehdr.e_shoff = sh_offset;
+ ehdr.e_shentsize = sizeof(Shdr) + 100;
+ ehdr.e_shnum = 4;
+ memcpy(&buffer[file_offset], &ehdr, sizeof(ehdr));
+
+ ASSERT_TRUE(android::base::WriteFully(fd, buffer.data(), buffer.size()));
+ }
+
static void SetUpTestCase() {
std::vector<uint8_t> buffer(1024);
+ memset(buffer.data(), 0, buffer.size());
memcpy(buffer.data(), ELFMAG, SELFMAG);
- for (size_t i = SELFMAG; i < buffer.size(); i++) {
- buffer[i] = i / 256 + 1;
- }
+ buffer[EI_CLASS] = ELFCLASS32;
ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
- for (size_t i = 0; i < 0x100; i++) {
- buffer[i] = i / 256 + 1;
- }
+ memset(buffer.data(), 0, buffer.size());
memcpy(&buffer[0x100], ELFMAG, SELFMAG);
- for (size_t i = 0x100 + SELFMAG; i < buffer.size(); i++) {
- buffer[i] = i / 256 + 1;
- }
+ buffer[0x100 + EI_CLASS] = ELFCLASS64;
ASSERT_TRUE(android::base::WriteFully(elf_at_100_.fd, buffer.data(), buffer.size()));
+
+ InitElf<Elf32_Ehdr, Elf32_Shdr>(elf32_at_map_.fd, 0x1000, 0x2000, ELFCLASS32);
+ InitElf<Elf64_Ehdr, Elf64_Shdr>(elf64_at_map_.fd, 0x2000, 0x3000, ELFCLASS64);
}
+ void SetUp() override {
+ memory_ = new MemoryFake;
+ process_memory_.reset(memory_);
+ }
+
+ MemoryFake* memory_;
+ std::shared_ptr<Memory> process_memory_;
+
static TemporaryFile elf_;
static TemporaryFile elf_at_100_;
+
+ static TemporaryFile elf32_at_map_;
+ static TemporaryFile elf64_at_map_;
};
TemporaryFile MapInfoCreateMemoryTest::elf_;
TemporaryFile MapInfoCreateMemoryTest::elf_at_100_;
+TemporaryFile MapInfoCreateMemoryTest::elf32_at_map_;
+TemporaryFile MapInfoCreateMemoryTest::elf64_at_map_;
TEST_F(MapInfoCreateMemoryTest, end_le_start) {
MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path};
- std::unique_ptr<Memory> memory;
- memory.reset(info.CreateMemory(getpid()));
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() == nullptr);
info.end = 0xff;
- memory.reset(info.CreateMemory(getpid()));
+ memory.reset(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() == nullptr);
// Make sure this test is valid.
info.end = 0x101;
- memory.reset(info.CreateMemory(getpid()));
+ memory.reset(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
}
@@ -85,7 +114,7 @@
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_.path};
- std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0x100U, info.elf_offset);
@@ -93,8 +122,9 @@
std::vector<uint8_t> buffer(1024);
ASSERT_TRUE(memory->Read(0, buffer.data(), 1024));
ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
- for (size_t i = SELFMAG; i < buffer.size(); i++) {
- ASSERT_EQ(i / 256 + 1, buffer[i]) << "Failed at byte " << i;
+ ASSERT_EQ(ELFCLASS32, buffer[EI_CLASS]);
+ for (size_t i = EI_CLASS + 1; i < buffer.size(); i++) {
+ ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
}
ASSERT_FALSE(memory->Read(1024, buffer.data(), 1));
@@ -105,7 +135,7 @@
TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_at_100_.path};
- std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
ASSERT_EQ(0U, info.elf_offset);
@@ -113,13 +143,50 @@
std::vector<uint8_t> buffer(0x100);
ASSERT_TRUE(memory->Read(0, buffer.data(), 0x100));
ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
- for (size_t i = SELFMAG; i < buffer.size(); i++) {
- ASSERT_EQ(2, buffer[i]) << "Failed at byte " << i;
+ ASSERT_EQ(ELFCLASS64, buffer[EI_CLASS]);
+ for (size_t i = EI_CLASS + 1; i < buffer.size(); i++) {
+ ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
}
ASSERT_FALSE(memory->Read(0x100, buffer.data(), 1));
}
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used. Further verify that if the
+// embedded elf is bigger than the initial map, the new object is larger
+// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
+ MapInfo info{.start = 0x5000, .end = 0x6000, .offset = 0x1000, .name = elf32_at_map_.path};
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ ASSERT_EQ(0U, info.elf_offset);
+
+ // Verify the memory is a valid elf.
+ uint8_t e_ident[SELFMAG + 1];
+ ASSERT_TRUE(memory->Read(0, e_ident, SELFMAG));
+ ASSERT_EQ(0, memcmp(e_ident, ELFMAG, SELFMAG));
+
+ // Read past the end of what would normally be the size of the map.
+ ASSERT_TRUE(memory->Read(0x1000, e_ident, 1));
+}
+
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
+ MapInfo info{.start = 0x7000, .end = 0x8000, .offset = 0x2000, .name = elf64_at_map_.path};
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
+ ASSERT_TRUE(memory.get() != nullptr);
+ ASSERT_EQ(0U, info.elf_offset);
+
+ // Verify the memory is a valid elf.
+ uint8_t e_ident[SELFMAG + 1];
+ ASSERT_TRUE(memory->Read(0, e_ident, SELFMAG));
+ ASSERT_EQ(0, memcmp(e_ident, ELFMAG, SELFMAG));
+
+ // Read past the end of what would normally be the size of the map.
+ ASSERT_TRUE(memory->Read(0x1000, e_ident, 1));
+}
+
// Verify that device file names will never result in Memory object creation.
TEST_F(MapInfoCreateMemoryTest, check_device_maps) {
// Set up some memory so that a valid local memory object would
@@ -129,81 +196,38 @@
info.start = reinterpret_cast<uint64_t>(buffer.data());
info.end = info.start + buffer.size();
info.offset = 0;
- std::unique_ptr<Memory> memory;
info.flags = 0x8000;
info.name = "/dev/something";
- memory.reset(info.CreateMemory(getpid()));
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() == nullptr);
}
-TEST_F(MapInfoCreateMemoryTest, local_memory) {
- // Set up some memory for a valid local memory object.
+TEST_F(MapInfoCreateMemoryTest, process_memory) {
+ MapInfo info;
+ info.start = 0x2000;
+ info.end = 0x3000;
+ info.offset = 0;
+
+ // Verify that the the process_memory object is used, so seed it
+ // with memory.
std::vector<uint8_t> buffer(1024);
for (size_t i = 0; i < buffer.size(); i++) {
buffer[i] = i % 256;
}
+ memory_->SetMemory(info.start, buffer.data(), buffer.size());
- MapInfo info;
- info.start = reinterpret_cast<uint64_t>(buffer.data());
- info.end = info.start + buffer.size();
- info.offset = 0;
-
- std::unique_ptr<Memory> memory;
- memory.reset(info.CreateMemory(getpid()));
+ std::unique_ptr<Memory> memory(info.CreateMemory(process_memory_));
ASSERT_TRUE(memory.get() != nullptr);
- std::vector<uint8_t> read_buffer(1024);
- ASSERT_TRUE(memory->Read(0, read_buffer.data(), read_buffer.size()));
- for (size_t i = 0; i < read_buffer.size(); i++) {
- ASSERT_EQ(i % 256, read_buffer[i]) << "Failed at byte " << i;
+ memset(buffer.data(), 0, buffer.size());
+ ASSERT_TRUE(memory->Read(0, buffer.data(), buffer.size()));
+ for (size_t i = 0; i < buffer.size(); i++) {
+ ASSERT_EQ(i % 256, buffer[i]) << "Failed at byte " << i;
}
- ASSERT_FALSE(memory->Read(read_buffer.size(), read_buffer.data(), 1));
-}
-
-TEST_F(MapInfoCreateMemoryTest, remote_memory) {
- std::vector<uint8_t> buffer(1024);
- memset(buffer.data(), 0xa, buffer.size());
-
- pid_t pid;
- if ((pid = fork()) == 0) {
- while (true)
- ;
- exit(1);
- }
- ASSERT_LT(0, pid);
-
- ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) != -1);
- uint64_t iterations = 0;
- siginfo_t si;
- while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) < 0 && errno == ESRCH) {
- usleep(30);
- iterations++;
- ASSERT_LT(iterations, 500000000ULL);
- }
-
- MapInfo info;
- info.start = reinterpret_cast<uint64_t>(buffer.data());
- info.end = info.start + buffer.size();
- info.offset = 0;
-
- std::unique_ptr<Memory> memory;
- memory.reset(info.CreateMemory(pid));
- ASSERT_TRUE(memory.get() != nullptr);
- // Set the local memory to a different value to guarantee we are reading
- // from the remote process.
- memset(buffer.data(), 0x1, buffer.size());
- std::vector<uint8_t> read_buffer(1024);
- ASSERT_TRUE(memory->Read(0, read_buffer.data(), read_buffer.size()));
- for (size_t i = 0; i < read_buffer.size(); i++) {
- ASSERT_EQ(0xaU, read_buffer[i]) << "Failed at byte " << i;
- }
-
- ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
-
- kill(pid, SIGKILL);
- ASSERT_EQ(pid, wait(nullptr));
+ // Try to read outside of the map size.
+ ASSERT_FALSE(memory->Read(buffer.size(), buffer.data(), 1));
}
} // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
index abfa172..0b70d13 100644
--- a/libunwindstack/tests/MapInfoGetElfTest.cpp
+++ b/libunwindstack/tests/MapInfoGetElfTest.cpp
@@ -32,43 +32,57 @@
#include <unwindstack/Elf.h>
#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include "ElfTestUtils.h"
+#include "MemoryFake.h"
namespace unwindstack {
class MapInfoGetElfTest : public ::testing::Test {
protected:
void SetUp() override {
- map_ = mmap(nullptr, kMapSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- ASSERT_NE(MAP_FAILED, map_);
-
- uint64_t start = reinterpret_cast<uint64_t>(map_);
- info_.reset(new MapInfo{.start = start, .end = start + 1024, .offset = 0, .name = ""});
+ memory_ = new MemoryFake;
+ process_memory_.reset(memory_);
}
- void TearDown() override { munmap(map_, kMapSize); }
+ template <typename Ehdr, typename Shdr>
+ static void InitElf(uint64_t sh_offset, Ehdr* ehdr, uint8_t class_type, uint8_t machine_type) {
+ memset(ehdr, 0, sizeof(*ehdr));
+ memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
+ ehdr->e_ident[EI_CLASS] = class_type;
+ ehdr->e_machine = machine_type;
+ ehdr->e_shoff = sh_offset;
+ ehdr->e_shentsize = sizeof(Shdr) + 100;
+ ehdr->e_shnum = 4;
+ }
const size_t kMapSize = 4096;
- void* map_ = nullptr;
- std::unique_ptr<MapInfo> info_;
+ std::shared_ptr<Memory> process_memory_;
+ MemoryFake* memory_;
+
+ TemporaryFile elf_;
};
TEST_F(MapInfoGetElfTest, invalid) {
+ MapInfo info{.start = 0x1000, .end = 0x2000, .offset = 0, .flags = PROT_READ, .name = ""};
+
// The map is empty, but this should still create an invalid elf object.
- std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf.get() != nullptr);
ASSERT_FALSE(elf->valid());
}
TEST_F(MapInfoGetElfTest, valid32) {
+ MapInfo info{.start = 0x3000, .end = 0x4000, .offset = 0, .flags = PROT_READ, .name = ""};
+
Elf32_Ehdr ehdr;
TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
- memcpy(map_, &ehdr, sizeof(ehdr));
+ memory_->SetMemory(0x3000, &ehdr, sizeof(ehdr));
- std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf.get() != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
@@ -76,11 +90,13 @@
}
TEST_F(MapInfoGetElfTest, valid64) {
+ MapInfo info{.start = 0x8000, .end = 0x9000, .offset = 0, .flags = PROT_READ, .name = ""};
+
Elf64_Ehdr ehdr;
TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
- memcpy(map_, &ehdr, sizeof(ehdr));
+ memory_->SetMemory(0x8000, &ehdr, sizeof(ehdr));
- std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf.get() != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
@@ -88,12 +104,14 @@
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) {
- TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(
- ELFCLASS32, EM_ARM, false, [&](uint64_t offset, const void* ptr, size_t size) {
- memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
- });
+ MapInfo info{.start = 0x4000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""};
- std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
+ TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(0x4000 + offset, ptr, size);
+ });
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf.get() != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
@@ -102,12 +120,14 @@
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
- TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(
- ELFCLASS64, EM_AARCH64, false, [&](uint64_t offset, const void* ptr, size_t size) {
- memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
- });
+ MapInfo info{.start = 0x6000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""};
- std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
+ TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(0x6000 + offset, ptr, size);
+ });
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
ASSERT_TRUE(elf.get() != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
@@ -116,12 +136,14 @@
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
- TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(
- ELFCLASS32, EM_ARM, true, [&](uint64_t offset, const void* ptr, size_t size) {
- memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
- });
+ MapInfo info{.start = 0x2000, .end = 0x3000, .offset = 0, .flags = PROT_READ, .name = ""};
- std::unique_ptr<Elf> elf(info_->GetElf(getpid(), true));
+ TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(0x2000 + offset, ptr, size);
+ });
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, true));
ASSERT_TRUE(elf.get() != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
@@ -130,12 +152,14 @@
}
TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
- TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(
- ELFCLASS64, EM_AARCH64, true, [&](uint64_t offset, const void* ptr, size_t size) {
- memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
- });
+ MapInfo info{.start = 0x5000, .end = 0x8000, .offset = 0, .flags = PROT_READ, .name = ""};
- std::unique_ptr<Elf> elf(info_->GetElf(getpid(), true));
+ TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(0x5000 + offset, ptr, size);
+ });
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, true));
ASSERT_TRUE(elf.get() != nullptr);
ASSERT_TRUE(elf->valid());
EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
@@ -143,4 +167,195 @@
EXPECT_TRUE(elf->gnu_debugdata_interface() != nullptr);
}
+TEST_F(MapInfoGetElfTest, end_le_start) {
+ MapInfo info{.start = 0x1000, .end = 0x1000, .offset = 0, .flags = PROT_READ, .name = elf_.path};
+
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, &ehdr, sizeof(ehdr)));
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+ ASSERT_FALSE(elf->valid());
+
+ info.elf = nullptr;
+ info.end = 0xfff;
+ elf.reset(info.GetElf(process_memory_, false));
+ ASSERT_FALSE(elf->valid());
+
+ // Make sure this test is valid.
+ info.elf = nullptr;
+ info.end = 0x2000;
+ elf.reset(info.GetElf(process_memory_, false));
+ ASSERT_TRUE(elf->valid());
+}
+
+// Verify that if the offset is non-zero but there is no elf at the offset,
+// that the full file is used.
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_full_file) {
+ MapInfo info{
+ .start = 0x1000, .end = 0x2000, .offset = 0x100, .flags = PROT_READ, .name = elf_.path};
+
+ std::vector<uint8_t> buffer(0x1000);
+ memset(buffer.data(), 0, buffer.size());
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memcpy(buffer.data(), &ehdr, sizeof(ehdr));
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+ ASSERT_TRUE(elf->valid());
+ ASSERT_TRUE(elf->memory() != nullptr);
+ ASSERT_EQ(0x100U, info.elf_offset);
+
+ // Read the entire file.
+ memset(buffer.data(), 0, buffer.size());
+ ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), buffer.size()));
+ ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+ for (size_t i = sizeof(ehdr); i < buffer.size(); i++) {
+ ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_FALSE(elf->memory()->Read(buffer.size(), buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used.
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file) {
+ MapInfo info{
+ .start = 0x1000, .end = 0x2000, .offset = 0x2000, .flags = PROT_READ, .name = elf_.path};
+
+ std::vector<uint8_t> buffer(0x4000);
+ memset(buffer.data(), 0, buffer.size());
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+ ASSERT_TRUE(elf->valid());
+ ASSERT_TRUE(elf->memory() != nullptr);
+ ASSERT_EQ(0U, info.elf_offset);
+
+ // Read the valid part of the file.
+ ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), 0x1000));
+ ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+ for (size_t i = sizeof(ehdr); i < 0x1000; i++) {
+ ASSERT_EQ(0, buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_FALSE(elf->memory()->Read(0x1000, buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used. Further verify that if the
+// embedded elf is bigger than the initial map, the new object is larger
+// than the original map size. Do this for a 32 bit elf and a 64 bit elf.
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf32) {
+ MapInfo info{
+ .start = 0x5000, .end = 0x6000, .offset = 0x1000, .flags = PROT_READ, .name = elf_.path};
+
+ std::vector<uint8_t> buffer(0x4000);
+ memset(buffer.data(), 0, buffer.size());
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ ehdr.e_shoff = 0x2000;
+ ehdr.e_shentsize = sizeof(Elf32_Shdr) + 100;
+ ehdr.e_shnum = 4;
+ memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+ ASSERT_TRUE(elf->valid());
+ ASSERT_TRUE(elf->memory() != nullptr);
+ ASSERT_EQ(0U, info.elf_offset);
+
+ // Verify the memory is a valid elf.
+ memset(buffer.data(), 0, buffer.size());
+ ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), 0x1000));
+ ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+
+ // Read past the end of what would normally be the size of the map.
+ ASSERT_TRUE(elf->memory()->Read(0x1000, buffer.data(), 1));
+}
+
+TEST_F(MapInfoGetElfTest, file_backed_non_zero_offset_partial_file_whole_elf64) {
+ MapInfo info{
+ .start = 0x7000, .end = 0x8000, .offset = 0x1000, .flags = PROT_READ, .name = elf_.path};
+
+ std::vector<uint8_t> buffer(0x4000);
+ memset(buffer.data(), 0, buffer.size());
+ Elf64_Ehdr ehdr;
+ TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
+ ehdr.e_shoff = 0x2000;
+ ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
+ ehdr.e_shnum = 4;
+ memcpy(&buffer[info.offset], &ehdr, sizeof(ehdr));
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+ ASSERT_TRUE(elf->valid());
+ ASSERT_TRUE(elf->memory() != nullptr);
+ ASSERT_EQ(0U, info.elf_offset);
+
+ // Verify the memory is a valid elf.
+ memset(buffer.data(), 0, buffer.size());
+ ASSERT_TRUE(elf->memory()->Read(0, buffer.data(), 0x1000));
+ ASSERT_EQ(0, memcmp(buffer.data(), &ehdr, sizeof(ehdr)));
+
+ // Read past the end of what would normally be the size of the map.
+ ASSERT_TRUE(elf->memory()->Read(0x1000, buffer.data(), 1));
+}
+
+TEST_F(MapInfoGetElfTest, process_memory_not_read_only) {
+ MapInfo info{.start = 0x9000, .end = 0xa000, .offset = 0x1000, .flags = 0, .name = ""};
+
+ // Create valid elf data in process memory only.
+ Elf64_Ehdr ehdr;
+ TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
+ ehdr.e_shoff = 0x2000;
+ ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
+ ehdr.e_shnum = 4;
+ memory_->SetMemory(0x9000, &ehdr, sizeof(ehdr));
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+ ASSERT_FALSE(elf->valid());
+
+ info.elf = nullptr;
+ info.flags = PROT_READ;
+ elf.reset(info.GetElf(process_memory_, false));
+ ASSERT_TRUE(elf->valid());
+}
+
+TEST_F(MapInfoGetElfTest, check_device_maps) {
+ MapInfo info{.start = 0x7000,
+ .end = 0x8000,
+ .offset = 0x1000,
+ .flags = PROT_READ | MAPS_FLAGS_DEVICE_MAP,
+ .name = "/dev/something"};
+
+ // Create valid elf data in process memory for this to verify that only
+ // the name is causing invalid elf data.
+ Elf64_Ehdr ehdr;
+ TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_X86_64);
+ ehdr.e_shoff = 0x2000;
+ ehdr.e_shentsize = sizeof(Elf64_Shdr) + 100;
+ ehdr.e_shnum = 4;
+ memory_->SetMemory(0x7000, &ehdr, sizeof(ehdr));
+
+ std::unique_ptr<Elf> elf(info.GetElf(process_memory_, false));
+ ASSERT_FALSE(elf->valid());
+
+ // Set the name to nothing to verify that it still fails.
+ info.elf = nullptr;
+ info.name = "";
+ elf.reset(info.GetElf(process_memory_, false));
+ ASSERT_FALSE(elf->valid());
+
+ // Change the flags and verify the elf is valid now.
+ info.elf = nullptr;
+ info.flags = PROT_READ;
+ elf.reset(info.GetElf(process_memory_, false));
+ ASSERT_TRUE(elf->valid());
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index 6d1366c..680fae9 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -31,10 +31,11 @@
TEST(MemoryRangeTest, read) {
std::vector<uint8_t> src(1024);
memset(src.data(), 0x4c, 1024);
- MemoryFake* memory = new MemoryFake;
- memory->SetMemory(9001, src);
+ MemoryFake* memory_fake = new MemoryFake;
+ std::shared_ptr<Memory> process_memory(memory_fake);
+ memory_fake->SetMemory(9001, src);
- MemoryRange range(memory, 9001, 9001 + src.size());
+ MemoryRange range(process_memory, 9001, 9001 + src.size());
std::vector<uint8_t> dst(1024);
ASSERT_TRUE(range.Read(0, dst.data(), src.size()));
@@ -46,10 +47,11 @@
TEST(MemoryRangeTest, read_near_limit) {
std::vector<uint8_t> src(4096);
memset(src.data(), 0x4c, 4096);
- MemoryFake* memory = new MemoryFake;
- memory->SetMemory(1000, src);
+ MemoryFake* memory_fake = new MemoryFake;
+ std::shared_ptr<Memory> process_memory(memory_fake);
+ memory_fake->SetMemory(1000, src);
- MemoryRange range(memory, 1000, 2024);
+ MemoryRange range(process_memory, 1000, 2024);
std::vector<uint8_t> dst(1024);
ASSERT_TRUE(range.Read(1020, dst.data(), 4));
@@ -69,7 +71,8 @@
TEST(MemoryRangeTest, read_overflow) {
std::vector<uint8_t> buffer(100);
- std::unique_ptr<MemoryRange> overflow(new MemoryRange(new MemoryFakeAlwaysReadZero, 100, 200));
+ std::shared_ptr<Memory> process_memory(new MemoryFakeAlwaysReadZero);
+ std::unique_ptr<MemoryRange> overflow(new MemoryRange(process_memory, 100, 200));
ASSERT_FALSE(overflow->Read(UINT64_MAX - 10, buffer.data(), 100));
}
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index f8965b2..a66d0c5 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -22,7 +22,6 @@
#include <sys/mman.h>
#include <sys/ptrace.h>
#include <sys/types.h>
-#include <time.h>
#include <unistd.h>
#include <vector>
@@ -34,32 +33,18 @@
#include <unwindstack/Memory.h>
#include "MemoryFake.h"
+#include "TestUtils.h"
namespace unwindstack {
class MemoryRemoteTest : public ::testing::Test {
protected:
- static uint64_t NanoTime() {
- struct timespec t = { 0, 0 };
- clock_gettime(CLOCK_MONOTONIC, &t);
- return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec);
- }
-
static bool Attach(pid_t pid) {
if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
return false;
}
- uint64_t start = NanoTime();
- siginfo_t si;
- while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) < 0 && errno == ESRCH) {
- if ((NanoTime() - start) > 10 * NS_PER_SEC) {
- printf("%d: Failed to stop after 10 seconds.\n", pid);
- return false;
- }
- usleep(30);
- }
- return true;
+ return TestQuiescePid(pid);
}
static bool Detach(pid_t pid) {
@@ -79,6 +64,7 @@
exit(1);
}
ASSERT_LT(0, pid);
+ TestScopedPidReaper reap(pid);
ASSERT_TRUE(Attach(pid));
@@ -91,9 +77,6 @@
}
ASSERT_TRUE(Detach(pid));
-
- kill(pid, SIGKILL);
- ASSERT_EQ(pid, wait(nullptr));
}
TEST_F(MemoryRemoteTest, read_fail) {
@@ -111,6 +94,7 @@
exit(1);
}
ASSERT_LT(0, pid);
+ TestScopedPidReaper reap(pid);
ASSERT_TRUE(Attach(pid));
@@ -132,9 +116,6 @@
ASSERT_EQ(0, munmap(src, pagesize));
ASSERT_TRUE(Detach(pid));
-
- kill(pid, SIGKILL);
- ASSERT_EQ(pid, wait(nullptr));
}
TEST_F(MemoryRemoteTest, read_overflow) {
@@ -152,6 +133,7 @@
exit(1);
}
ASSERT_LT(0, pid);
+ TestScopedPidReaper reap(pid);
ASSERT_TRUE(Attach(pid));
@@ -162,9 +144,6 @@
ASSERT_FALSE(remote.Read(0, dst.data(), 100));
ASSERT_TRUE(Detach(pid));
-
- kill(pid, SIGKILL);
- ASSERT_EQ(pid, wait(nullptr));
}
} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index c76ecaa..efcd029 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -24,19 +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 f549a50..3b9f92b 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -23,67 +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*) 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 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());
@@ -106,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());
@@ -128,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()));
@@ -192,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()));
@@ -200,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()));
@@ -210,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/TestUtils.h b/libunwindstack/tests/TestUtils.h
new file mode 100644
index 0000000..8c31aa6
--- /dev/null
+++ b/libunwindstack/tests/TestUtils.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 _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
+#define _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
+
+#include <signal.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+namespace unwindstack {
+
+class TestScopedPidReaper {
+ public:
+ TestScopedPidReaper(pid_t pid) : pid_(pid) {}
+ ~TestScopedPidReaper() {
+ kill(pid_, SIGKILL);
+ waitpid(pid_, nullptr, 0);
+ }
+
+ private:
+ pid_t pid_;
+};
+
+inline bool TestQuiescePid(pid_t pid) {
+ siginfo_t si;
+ bool ready = false;
+ // Wait for up to 5 seconds.
+ for (size_t i = 0; i < 5000; i++) {
+ if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+ ready = true;
+ break;
+ }
+ usleep(1000);
+ }
+ return ready;
+}
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_TESTS_TEST_UTILS_H
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index 2fc3a38..9f9ca8b 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -15,10 +15,9 @@
*/
#include <errno.h>
-#include <string.h>
-
#include <signal.h>
#include <stdint.h>
+#include <string.h>
#include <sys/ptrace.h>
#include <sys/syscall.h>
#include <unistd.h>
@@ -32,26 +31,37 @@
#include <thread>
#include <vector>
-#include <unwindstack/Elf.h>
-#include <unwindstack/MapInfo.h>
+#include <android-base/stringprintf.h>
+
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
#include <unwindstack/RegsGetLocal.h>
+#include <unwindstack/Unwinder.h>
+
+#include "TestUtils.h"
namespace unwindstack {
-static std::atomic_bool g_ready(false);
-static volatile bool g_ready_for_remote = false;
-static volatile bool g_signal_ready_for_remote = false;
-static std::atomic_bool g_finish(false);
+static std::atomic_bool g_ready;
+static volatile bool g_ready_for_remote;
+static volatile bool g_signal_ready_for_remote;
+static std::atomic_bool g_finish;
static std::atomic_uintptr_t g_ucontext;
-static std::vector<const char*> kFunctionOrder{"InnerFunction", "MiddleFunction", "OuterFunction"};
+static void ResetGlobals() {
+ g_ready = false;
+ g_ready_for_remote = false;
+ g_signal_ready_for_remote = false;
+ g_finish = false;
+ g_ucontext = 0;
+}
-static std::vector<const char*> kFunctionSignalOrder{"SignalInnerFunction", "SignalMiddleFunction",
- "SignalOuterFunction", "InnerFunction",
- "MiddleFunction", "OuterFunction"};
+static std::vector<const char*> kFunctionOrder{"OuterFunction", "MiddleFunction", "InnerFunction"};
+
+static std::vector<const char*> kFunctionSignalOrder{"OuterFunction", "MiddleFunction",
+ "InnerFunction", "SignalOuterFunction",
+ "SignalMiddleFunction", "SignalInnerFunction"};
static void SignalHandler(int, siginfo_t*, void* sigcontext) {
g_ucontext = reinterpret_cast<uintptr_t>(sigcontext);
@@ -77,127 +87,117 @@
SignalOuterFunction();
}
-static std::string ErrorMsg(const std::vector<const char*>& function_names, size_t index,
- std::stringstream& unwind_stream) {
+static std::string ErrorMsg(const std::vector<const char*>& function_names, Unwinder& unwinder) {
+ std::string unwind;
+ for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+ unwind += unwinder.FormatFrame(i) + '\n';
+ }
+
return std::string(
"Unwind completed without finding all frames\n"
" Looking for function: ") +
- function_names[index] + "\n" + "Unwind data:\n" + unwind_stream.str();
+ function_names.front() + "\n" + "Unwind data:\n" + unwind;
}
-static void VerifyUnwind(pid_t pid, Memory* memory, Maps* maps, Regs* regs,
- std::vector<const char*>& function_names) {
- size_t function_name_index = 0;
+static void VerifyUnwind(pid_t pid, Maps* maps, Regs* regs,
+ std::vector<const char*> expected_function_names) {
+ auto process_memory(Memory::CreateProcessMemory(pid));
- std::stringstream unwind_stream;
- unwind_stream << std::hex;
- for (size_t frame_num = 0; frame_num < 64; frame_num++) {
- ASSERT_NE(0U, regs->pc()) << ErrorMsg(function_names, function_name_index, unwind_stream);
- MapInfo* map_info = maps->Find(regs->pc());
- ASSERT_TRUE(map_info != nullptr) << ErrorMsg(function_names, function_name_index, unwind_stream);
+ Unwinder unwinder(512, maps, regs, process_memory);
+ unwinder.Unwind();
- Elf* elf = map_info->GetElf(pid, true);
- uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
- uint64_t adjusted_rel_pc = rel_pc;
- if (frame_num != 0) {
- adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
- }
- unwind_stream << " PC: 0x" << regs->pc() << " Rel: 0x" << adjusted_rel_pc;
- unwind_stream << " Map: ";
- if (!map_info->name.empty()) {
- unwind_stream << map_info->name;
- } else {
- unwind_stream << " anonymous";
- }
- unwind_stream << "<" << map_info->start << "-" << map_info->end << ">";
-
- std::string name;
- uint64_t func_offset;
- if (elf->GetFunctionName(adjusted_rel_pc, &name, &func_offset)) {
- if (name == function_names[function_name_index]) {
- if (++function_name_index == function_names.size()) {
- return;
- }
+ std::string expected_function = expected_function_names.back();
+ expected_function_names.pop_back();
+ for (auto& frame : unwinder.frames()) {
+ if (frame.function_name == expected_function) {
+ if (expected_function_names.empty()) {
+ break;
}
- unwind_stream << " " << name;
+ expected_function = expected_function_names.back();
+ expected_function_names.pop_back();
}
- unwind_stream << "\n";
- ASSERT_TRUE(elf->Step(rel_pc + map_info->elf_offset, regs, memory))
- << ErrorMsg(function_names, function_name_index, unwind_stream);
}
- ASSERT_TRUE(false) << ErrorMsg(function_names, function_name_index, unwind_stream);
+
+ ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, unwinder);
}
// This test assumes that this code is compiled with optimizations turned
// off. If this doesn't happen, then all of the calls will be optimized
// away.
-extern "C" void InnerFunction(bool local) {
+extern "C" void InnerFunction(bool local, bool trigger_invalid_call) {
if (local) {
LocalMaps maps;
ASSERT_TRUE(maps.Parse());
std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
RegsGetLocal(regs.get());
- MemoryLocal memory;
- VerifyUnwind(getpid(), &memory, &maps, regs.get(), kFunctionOrder);
+ VerifyUnwind(getpid(), &maps, regs.get(), kFunctionOrder);
} else {
g_ready_for_remote = true;
g_ready = true;
+ if (trigger_invalid_call) {
+ void (*crash_func)() = nullptr;
+ crash_func();
+ }
while (!g_finish.load()) {
}
}
}
-extern "C" void MiddleFunction(bool local) {
- InnerFunction(local);
+extern "C" void MiddleFunction(bool local, bool trigger_invalid_call) {
+ InnerFunction(local, trigger_invalid_call);
}
-extern "C" void OuterFunction(bool local) {
- MiddleFunction(local);
+extern "C" void OuterFunction(bool local, bool trigger_invalid_call) {
+ MiddleFunction(local, trigger_invalid_call);
}
-TEST(UnwindTest, local) {
- OuterFunction(true);
+class UnwindTest : public ::testing::Test {
+ public:
+ void SetUp() override { ResetGlobals(); }
+};
+
+TEST_F(UnwindTest, local) {
+ OuterFunction(true, false);
}
void WaitForRemote(pid_t pid, uint64_t addr, bool leave_attached, bool* completed) {
*completed = false;
// Need to sleep before attempting first ptrace. Without this, on the
- // host it becomes impossible to attach and ptrace set errno to EPERM.
+ // host it becomes impossible to attach and ptrace sets errno to EPERM.
usleep(1000);
- for (size_t i = 0; i < 100; i++) {
- ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, 0, 0));
- for (size_t j = 0; j < 100; j++) {
- siginfo_t si;
- if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
- MemoryRemote memory(pid);
- // Read the remote value to see if we are ready.
- bool value;
- if (memory.Read(addr, &value, sizeof(value)) && value) {
- *completed = true;
- break;
- }
+ for (size_t i = 0; i < 1000; i++) {
+ if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
+ ASSERT_TRUE(TestQuiescePid(pid))
+ << "Waiting for process to quiesce failed: " << strerror(errno);
+
+ MemoryRemote memory(pid);
+ // Read the remote value to see if we are ready.
+ bool value;
+ if (memory.Read(addr, &value, sizeof(value)) && value) {
+ *completed = true;
}
- usleep(1000);
+ if (!*completed || !leave_attached) {
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
+ }
+ if (*completed) {
+ break;
+ }
+ } else {
+ ASSERT_EQ(ESRCH, errno) << "ptrace attach failed with unexpected error: " << strerror(errno);
}
- if (leave_attached && *completed) {
- break;
- }
- ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
- if (*completed) {
- break;
- }
- usleep(1000);
+ usleep(5000);
}
}
-TEST(UnwindTest, remote) {
+TEST_F(UnwindTest, remote) {
pid_t pid;
if ((pid = fork()) == 0) {
- OuterFunction(false);
+ OuterFunction(false, false);
exit(0);
}
ASSERT_NE(-1, pid);
+ TestScopedPidReaper reap(pid);
bool completed;
WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), true, &completed);
@@ -205,23 +205,20 @@
RemoteMaps maps(pid);
ASSERT_TRUE(maps.Parse());
- MemoryRemote memory(pid);
std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
ASSERT_TRUE(regs.get() != nullptr);
- VerifyUnwind(pid, &memory, &maps, regs.get(), kFunctionOrder);
+ VerifyUnwind(pid, &maps, regs.get(), kFunctionOrder);
- ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
-
- kill(pid, SIGKILL);
- ASSERT_EQ(pid, wait(nullptr));
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
+ << "ptrace detach failed with unexpected error: " << strerror(errno);
}
-TEST(UnwindTest, from_context) {
+TEST_F(UnwindTest, from_context) {
std::atomic_int tid(0);
std::thread thread([&]() {
tid = syscall(__NR_gettid);
- OuterFunction(false);
+ OuterFunction(false, false);
});
struct sigaction act, oldact;
@@ -254,9 +251,8 @@
LocalMaps maps;
ASSERT_TRUE(maps.Parse());
std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::CurrentMachineType(), ucontext));
- MemoryLocal memory;
- VerifyUnwind(tid.load(), &memory, &maps, regs.get(), kFunctionOrder);
+ VerifyUnwind(getpid(), &maps, regs.get(), kFunctionOrder);
ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
@@ -264,51 +260,55 @@
thread.join();
}
-static void RemoteThroughSignal(unsigned int sa_flags) {
- g_ready = false;
- g_signal_ready_for_remote = false;
- g_finish = false;
-
+static void RemoteThroughSignal(int signal, unsigned int sa_flags) {
pid_t pid;
if ((pid = fork()) == 0) {
struct sigaction act, oldact;
memset(&act, 0, sizeof(act));
act.sa_sigaction = SignalCallerHandler;
act.sa_flags = SA_RESTART | SA_ONSTACK | sa_flags;
- ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
+ ASSERT_EQ(0, sigaction(signal, &act, &oldact));
- OuterFunction(false);
+ OuterFunction(false, signal == SIGSEGV);
exit(0);
}
ASSERT_NE(-1, pid);
+ TestScopedPidReaper reap(pid);
bool completed;
- WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), false, &completed);
- ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
- ASSERT_EQ(0, kill(pid, SIGUSR1));
+ if (signal != SIGSEGV) {
+ WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), false, &completed);
+ ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
+ ASSERT_EQ(0, kill(pid, SIGUSR1));
+ }
WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_signal_ready_for_remote), true, &completed);
ASSERT_TRUE(completed) << "Timed out waiting for remote process to be in signal handler.";
RemoteMaps maps(pid);
ASSERT_TRUE(maps.Parse());
- MemoryRemote memory(pid);
std::unique_ptr<Regs> regs(Regs::RemoteGet(pid));
ASSERT_TRUE(regs.get() != nullptr);
- VerifyUnwind(pid, &memory, &maps, regs.get(), kFunctionSignalOrder);
+ VerifyUnwind(pid, &maps, regs.get(), kFunctionSignalOrder);
- ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
-
- kill(pid, SIGKILL);
- ASSERT_EQ(pid, wait(nullptr));
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0))
+ << "ptrace detach failed with unexpected error: " << strerror(errno);
}
-TEST(UnwindTest, remote_through_signal) {
- RemoteThroughSignal(0);
+TEST_F(UnwindTest, remote_through_signal) {
+ RemoteThroughSignal(SIGUSR1, 0);
}
-TEST(UnwindTest, remote_through_signal_sa_siginfo) {
- RemoteThroughSignal(SA_SIGINFO);
+TEST_F(UnwindTest, remote_through_signal_sa_siginfo) {
+ RemoteThroughSignal(SIGUSR1, SA_SIGINFO);
+}
+
+TEST_F(UnwindTest, remote_through_signal_with_invalid_func) {
+ RemoteThroughSignal(SIGSEGV, 0);
+}
+
+TEST_F(UnwindTest, remote_through_signal_sa_siginfo_with_invalid_func) {
+ RemoteThroughSignal(SIGSEGV, SA_SIGINFO);
}
} // namespace unwindstack
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 c1077f8..f2530d7 100644
--- a/libunwindstack/tools/unwind.cpp
+++ b/libunwindstack/tools/unwind.cpp
@@ -26,13 +26,11 @@
#include <sys/types.h>
#include <unistd.h>
-#include <string>
-
#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) {
@@ -51,10 +49,6 @@
return false;
}
-static bool Detach(pid_t pid) {
- return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
-}
-
void DoUnwind(pid_t pid) {
unwindstack::RemoteMaps remote_maps(pid);
if (!remote_maps.Parse()) {
@@ -68,7 +62,6 @@
return;
}
- bool bits32 = true;
printf("ABI: ");
switch (regs->MachineType()) {
case EM_ARM:
@@ -79,11 +72,9 @@
break;
case EM_AARCH64:
printf("arm64");
- bits32 = false;
break;
case EM_X86_64:
printf("x86_64");
- bits32 = false;
break;
default:
printf("unknown\n");
@@ -91,53 +82,13 @@
}
printf("\n");
- unwindstack::MemoryRemote remote_memory(pid);
- for (size_t frame_num = 0; frame_num < 64; frame_num++) {
- if (regs->pc() == 0) {
- break;
- }
- unwindstack::MapInfo* map_info = remote_maps.Find(regs->pc());
- if (map_info == nullptr) {
- printf("Failed to find map data for the pc\n");
- break;
- }
+ auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
+ unwindstack::Unwinder unwinder(128, &remote_maps, regs, process_memory);
+ unwinder.Unwind();
- unwindstack::Elf* elf = map_info->GetElf(pid, true);
-
- uint64_t 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 name;
- if (bits32) {
- printf(" #%02zu pc %08" PRIx64, frame_num, adjusted_rel_pc);
- } else {
- printf(" #%02zu pc %016" PRIx64, frame_num, adjusted_rel_pc);
- }
- if (!map_info->name.empty()) {
- printf(" %s", map_info->name.c_str());
- if (map_info->elf_offset != 0) {
- printf(" (offset 0x%" PRIx64 ")", map_info->elf_offset);
- }
- } else {
- printf(" <anonymous:%" PRIx64 ">", map_info->offset);
- }
- uint64_t func_offset;
- if (elf->GetFunctionName(adjusted_rel_pc, &name, &func_offset)) {
- printf(" (%s", name.c_str());
- if (func_offset != 0) {
- printf("+%" PRId64, func_offset);
- }
- printf(")");
- }
- printf("\n");
-
- if (!elf->Step(rel_pc + map_info->elf_offset, regs, &remote_memory)) {
- break;
- }
+ // Print the frames.
+ for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+ printf("%s\n", unwinder.FormatFrame(i).c_str());
}
}
@@ -155,7 +106,7 @@
DoUnwind(pid);
- Detach(pid);
+ ptrace(PTRACE_DETACH, pid, 0, 0);
return 0;
}
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
index b757c1e..dc9ae5a 100644
--- a/libunwindstack/tools/unwind_symbols.cpp
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -28,8 +28,11 @@
#include <unwindstack/Memory.h>
int main(int argc, char** argv) {
- if (argc != 2) {
- printf("Need to pass the name of an elf file to the program.\n");
+ if (argc != 2 && argc != 3) {
+ printf("Usage: unwind_symbols <ELF_FILE> [<FUNC_ADDRESS>]\n");
+ printf(" Dump all function symbols in ELF_FILE. If FUNC_ADDRESS is\n");
+ printf(" specified, then get the function at that address.\n");
+ printf(" FUNC_ADDRESS must be a hex number.\n");
return 1;
}
@@ -43,6 +46,16 @@
return 1;
}
+ uint64_t func_addr;
+ if (argc == 3) {
+ char* name;
+ func_addr = strtoull(argv[2], &name, 16);
+ if (*name != '\0') {
+ printf("%s is not a hex number.\n", argv[2]);
+ return 1;
+ }
+ }
+
// Send all log messages to stdout.
unwindstack::log_to_stdout(true);
@@ -76,9 +89,24 @@
return 1;
}
- // This is a crude way to get the symbols in order.
std::string name;
uint64_t load_bias = elf.interface()->load_bias();
+ if (argc == 3) {
+ std::string cur_name;
+ uint64_t func_offset;
+ if (!elf.GetFunctionName(func_addr, &cur_name, &func_offset)) {
+ printf("No known function at 0x%" PRIx64 "\n", func_addr);
+ return 1;
+ }
+ printf("<0x%" PRIx64 ">", func_addr - func_offset);
+ if (func_offset != 0) {
+ printf("+%" PRId64, func_offset);
+ }
+ printf(": %s\n", cur_name.c_str());
+ return 0;
+ }
+
+ // This is a crude way to get the symbols in order.
for (const auto& entry : elf.interface()->pt_loads()) {
uint64_t start = entry.second.offset + load_bias;
uint64_t end = entry.second.table_size + load_bias;
diff --git a/libusbhost/Android.bp b/libusbhost/Android.bp
index a0d6b9b..fc6f305 100644
--- a/libusbhost/Android.bp
+++ b/libusbhost/Android.bp
@@ -16,6 +16,10 @@
cc_library {
name: "libusbhost",
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
host_supported: true,
srcs: ["usbhost.c"],
cflags: ["-Werror"],
diff --git a/libutils/Android.bp b/libutils/Android.bp
index adcde81..30e6b49 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -18,10 +18,12 @@
host_supported: true,
header_libs: [
+ "liblog_headers",
"libsystem_headers",
"libcutils_headers"
],
export_header_lib_headers: [
+ "liblog_headers",
"libsystem_headers",
"libcutils_headers"
],
@@ -119,7 +121,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/SystemClock.cpp b/libutils/SystemClock.cpp
index 28fc351..73ec1be 100644
--- a/libutils/SystemClock.cpp
+++ b/libutils/SystemClock.cpp
@@ -23,9 +23,9 @@
#include <utils/SystemClock.h>
-#include <sys/time.h>
#include <string.h>
#include <errno.h>
+#include <time.h>
#include <cutils/compiler.h>
diff --git a/libutils/include/utils/StrongPointer.h b/libutils/include/utils/StrongPointer.h
index 0c20607..ae6d9c8 100644
--- a/libutils/include/utils/StrongPointer.h
+++ b/libutils/include/utils/StrongPointer.h
@@ -82,9 +82,10 @@
// Accessors
- inline T& operator* () const { return *m_ptr; }
- inline T* operator-> () const { return m_ptr; }
- inline T* get() const { return m_ptr; }
+ inline T& operator* () const { return *m_ptr; }
+ inline T* operator-> () const { return m_ptr; }
+ inline T* get() const { return m_ptr; }
+ inline explicit operator bool () const { return m_ptr != nullptr; }
// Operators
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 333835c..e3faee3 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -58,6 +58,9 @@
name: "libziparchive",
host_supported: true,
vendor_available: true,
+ vndk: {
+ enabled: true,
+ },
defaults: [
"libziparchive_defaults",
@@ -66,11 +69,11 @@
shared_libs: [
"liblog",
"libbase",
+ "libz",
],
target: {
android: {
shared_libs: [
- "libz",
"libutils",
],
},
@@ -78,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"],
- },
},
}
@@ -102,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 007dfba..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 {
@@ -56,22 +56,27 @@
if (argc > optind)
optarg = argv[optind];
+ if (!optarg || !optarg[0]) optarg = "shell";
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/Android.mk b/rootdir/Android.mk
index 48a46c6..e199ed4 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -127,7 +127,7 @@
#
# create some directories (some are mount points) and symlinks
LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
- sbin dev proc sys system data oem acct config storage mnt root $(BOARD_ROOT_EXTRA_FOLDERS)); \
+ sbin dev proc sys system data oem acct config storage mnt $(BOARD_ROOT_EXTRA_FOLDERS)); \
ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \
ln -sf /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
diff --git a/rootdir/init.rc b/rootdir/init.rc
index fb1fbd4..d503ffe 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -721,6 +721,7 @@
user shell
group shell log readproc
seclabel u:r:shell:s0
+ setenv HOSTNAME console
on property:ro.debuggable=1
# Give writes to anyone for the trace folder on debug builds.
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index 915d159..3168f40 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -12,7 +12,7 @@
mkdir /data/adb 0700 root root
# adbd is controlled via property triggers in init.<platform>.usb.rc
-service adbd /sbin/adbd --root_seclabel=u:r:su:s0
+service adbd /system/bin/adbd --root_seclabel=u:r:su:s0
class core
socket adbd stream 660 system system
disabled
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/shell_and_utilities/README.md b/shell_and_utilities/README.md
index 5d10c18..c4e8aac 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -4,20 +4,25 @@
Since IceCreamSandwich Android has used
[mksh](https://www.mirbsd.org/mksh.htm) as its shell. Before then it used
[ash](https://en.wikipedia.org/wiki/Almquist_shell) (which actually
-remained in the tree up to and including KitKat).
+remained unused in the tree up to and including KitKat).
-Initially Android had a very limited command-line provided by its
-own "toolbox" binary. These days almost everything is supplied by
+Initially Android had a very limited command-line provided by its own
+"toolbox" binary. Since Marshmallow almost everything is supplied by
[toybox](http://landley.net/toybox/) instead.
We started moving a few of the more important tools to full
-BSD implementations in JellyBean before we started in earnest in
+BSD implementations in JellyBean, and continued this work in
Lollipop. Lollipop was a major break with the past in many ways (LP64
support and the switch to ART both having lots of knock-on effects around
the system), so although this was the beginning of the end of toolbox it
(a) didn't stand out given all the other systems-level changes and (b)
in Marshmallow we changed direction and started the move to toybox.
+Not everything is provided by toybox, though. We currently still use
+the BSD dd and grep (because the toybox versions are still unfinished),
+and for the bzip2 command-line tools we use the ones that are part of
+the bzip2 distribution.
+
The lists below show what tools were provided and where they came from in
each release starting with Gingerbread. This doesn't tell the full story,
because the toolbox implementations did have bugs fixed and options added
@@ -25,6 +30,10 @@
`-f`. But this gives you an idea of what was available in any given release,
and how usable it was likely to be.
+Also note that in any given release `toybox` probably contains more
+commands than there are symlinks for in `/system/bin`. You can get the
+full list for a release by running `toybox` directly.
+
Android 2.3 (Gingerbread)
-------------------------
@@ -132,26 +141,26 @@
uptime usleep vmstat wc which whoami xargs xxd yes
-Current AOSP
-------------
+Android 8.0 (Oreo)
+------------------
BSD: dd grep
bzip2: bzcat bzip2 bunzip2
-toolbox: getevent gzip newfs\_msdos gunzip zcat
+toolbox: getevent newfs\_msdos
toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
dos2unix du echo env expand expr fallocate false file find flock free
-getenforce getprop groups head hostname hwclock id ifconfig inotifyd
-insmod ionice iorenice kill killall ln load\_policy log logname losetup
-ls lsmod lsof lsusb md5sum microcom mkdir mknod mkswap mktemp modinfo
-modprobe more mount mountpoint mv netstat nice nl nohup od paste patch
-pgrep pidof pkill pmap printenv printf ps pwd readlink realpath renice
-restorecon rm rmdir rmmod runcon sed sendevent seq setenforce setprop
-setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep sort split
-start stat stop strings swapoff swapon sync sysctl tac tail tar taskset
-tee time timeout top touch tr true truncate tty ulimit umount uname uniq
-unix2dos uptime usleep uudecode uuencode vmstat wc which whoami xargs
-xxd yes
+getenforce getprop groups gunzip gzip head hostname hwclock id ifconfig
+inotifyd insmod ionice iorenice kill killall ln load\_policy log logname
+losetup ls lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod
+mkswap mktemp modinfo modprobe more mount mountpoint mv netstat nice
+nl nohup od paste patch pgrep pidof pkill pmap printenv printf ps pwd
+readlink realpath renice restorecon rm rmdir rmmod runcon sed sendevent
+seq setenforce setprop setsid sha1sum sha224sum sha256sum sha384sum
+sha512sum sleep sort split start stat stop strings swapoff swapon sync
+sysctl tac tail tar taskset tee time timeout top touch tr true truncate
+tty ulimit umount uname uniq unix2dos uptime usleep uudecode uuencode
+vmstat wc which whoami xargs xxd yes zcat
diff --git a/trusty/keymaster/Android.bp b/trusty/keymaster/Android.bp
index 3b6867c..6b9d723 100644
--- a/trusty/keymaster/Android.bp
+++ b/trusty/keymaster/Android.bp
@@ -33,7 +33,8 @@
shared_libs: [
"libcrypto",
"libcutils",
- "libkeymaster1",
+ "libkeymaster_portable",
+ "libkeymaster_staging",
"libtrusty",
"libkeymaster_messages",
"libsoftkeymasterdevice",
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,
diff --git a/trusty/nvram/trusty_nvram_device.cpp b/trusty/nvram/trusty_nvram_device.cpp
index 2c50915..82a1228 100644
--- a/trusty/nvram/trusty_nvram_device.cpp
+++ b/trusty/nvram/trusty_nvram_device.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <errno.h>
+
#include <nvram/hal/nvram_device_adapter.h>
#include "trusty_nvram_implementation.h"