Merge "adb: Open FunctionFS files with more restrictive access modes"
diff --git a/adb/adb.h b/adb/adb.h
index a6d0463..c74fa99 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -196,10 +196,6 @@
extern const char* adb_device_banner;
-#if !ADB_HOST
-extern int SHELL_EXIT_NOTIFY_FD;
-#endif // !ADB_HOST
-
#define CHUNK_SIZE (64 * 1024)
#if !ADB_HOST
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index a142384..fecf452 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -29,6 +29,7 @@
#include "socket_spec.h"
#include "sysdeps.h"
+#include "sysdeps/memory.h"
#include "transport.h"
// A listener is an entity which binds to a local port and, upon receiving a connection on that
@@ -203,7 +204,7 @@
}
}
- std::unique_ptr<alistener> listener(new alistener(local_name, connect_to));
+ auto listener = std::make_unique<alistener>(local_name, connect_to);
int resolved = 0;
listener->fd = socket_spec_listen(listener->local_name, error, &resolved);
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index d126f52..34930c6 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -61,6 +61,7 @@
#include "services.h"
#include "shell_service.h"
#include "sysdeps/chrono.h"
+#include "sysdeps/memory.h"
static int install_app(int argc, const char** argv);
static int install_multiple_app(int argc, const char** argv);
@@ -263,7 +264,7 @@
char raw_buffer[BUFSIZ];
char* buffer_ptr = raw_buffer;
if (use_shell_protocol) {
- protocol.reset(new ShellProtocol(fd));
+ protocol = std::make_unique<ShellProtocol>(fd);
if (!protocol) {
LOG(ERROR) << "failed to allocate memory for ShellProtocol object";
return 1;
@@ -630,7 +631,7 @@
args->raw_stdin = raw_stdin;
args->escape_char = escape_char;
if (use_shell_protocol) {
- args->protocol.reset(new ShellProtocol(args->write_fd));
+ args->protocol = std::make_unique<ShellProtocol>(args->write_fd);
}
if (raw_stdin) stdin_raw_init();
diff --git a/adb/daemon/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp
index 49e0363..0fcf89b 100644
--- a/adb/daemon/set_verity_enable_state_service.cpp
+++ b/adb/daemon/set_verity_enable_state_service.cpp
@@ -98,13 +98,22 @@
return android::base::GetProperty("ro.boot.slot_suffix", "");
}
+static bool is_avb_device_locked() {
+ return android::base::GetProperty("ro.boot.vbmeta.device_state", "") == "locked";
+}
+
/* Use AVB to turn verity on/off */
static bool set_avb_verity_enabled_state(int fd, AvbOps* ops, bool enable_verity) {
std::string ab_suffix = get_ab_suffix();
-
bool verity_enabled;
+
+ if (is_avb_device_locked()) {
+ WriteFdFmt(fd, "Device is locked. Please unlock the device first\n");
+ return false;
+ }
+
if (!avb_user_verity_get(ops, ab_suffix.c_str(), &verity_enabled)) {
- WriteFdFmt(fd, "Error getting verity state\n");
+ WriteFdFmt(fd, "Error getting verity state. Try adb root first?\n");
return false;
}
diff --git a/adb/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index f9f80c0..17c7eba 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -372,8 +372,8 @@
}
D("protocol FD = %d", protocol_sfd_.get());
- input_.reset(new ShellProtocol(protocol_sfd_));
- output_.reset(new ShellProtocol(protocol_sfd_));
+ input_ = std::make_unique<ShellProtocol>(protocol_sfd_);
+ output_ = std::make_unique<ShellProtocol>(protocol_sfd_);
if (!input_ || !output_) {
*error = "failed to allocate shell protocol objects";
kill(pid_, SIGKILL);
@@ -681,22 +681,6 @@
}
protocol_sfd_.reset(-1);
}
-
- // Pass the local socket FD to the shell cleanup fdevent.
- if (SHELL_EXIT_NOTIFY_FD >= 0) {
- int fd = local_socket_sfd_;
- if (WriteFdExactly(SHELL_EXIT_NOTIFY_FD, &fd, sizeof(fd))) {
- D("passed fd %d to SHELL_EXIT_NOTIFY_FD (%d) for pid %d",
- fd, SHELL_EXIT_NOTIFY_FD, pid_);
- // The shell exit fdevent now owns the FD and will close it once
- // the last bit of data flushes through.
- static_cast<void>(local_socket_sfd_.release());
- } else {
- PLOG(ERROR) << "failed to write fd " << fd
- << " to SHELL_EXIT_NOTIFY_FD (" << SHELL_EXIT_NOTIFY_FD
- << ") for pid " << pid_;
- }
- }
}
} // namespace
diff --git a/adb/daemon/shell_service_test.cpp b/adb/daemon/shell_service_test.cpp
index 839284e..4e27822 100644
--- a/adb/daemon/shell_service_test.cpp
+++ b/adb/daemon/shell_service_test.cpp
@@ -55,40 +55,20 @@
static sighandler_t saved_sigpipe_handler_;
int subprocess_fd_ = -1;
- int shell_exit_receiver_fd_ = -1, saved_shell_exit_fd_;
};
sighandler_t ShellServiceTest::saved_sigpipe_handler_ = nullptr;
void ShellServiceTest::StartTestSubprocess(
const char* command, SubprocessType type, SubprocessProtocol protocol) {
- // We want to intercept the shell exit message to make sure it's sent.
- saved_shell_exit_fd_ = SHELL_EXIT_NOTIFY_FD;
- int fd[2];
- ASSERT_TRUE(adb_socketpair(fd) >= 0);
- SHELL_EXIT_NOTIFY_FD = fd[0];
- shell_exit_receiver_fd_ = fd[1];
-
subprocess_fd_ = StartSubprocess(command, nullptr, type, protocol);
ASSERT_TRUE(subprocess_fd_ >= 0);
}
void ShellServiceTest::CleanupTestSubprocess() {
if (subprocess_fd_ >= 0) {
- // Subprocess should send its FD to SHELL_EXIT_NOTIFY_FD for cleanup.
- int notified_fd = -1;
- ASSERT_TRUE(ReadFdExactly(shell_exit_receiver_fd_, ¬ified_fd,
- sizeof(notified_fd)));
- ASSERT_EQ(notified_fd, subprocess_fd_);
-
adb_close(subprocess_fd_);
subprocess_fd_ = -1;
-
- // Restore SHELL_EXIT_NOTIFY_FD.
- adb_close(SHELL_EXIT_NOTIFY_FD);
- adb_close(shell_exit_receiver_fd_);
- shell_exit_receiver_fd_ = -1;
- SHELL_EXIT_NOTIFY_FD = saved_shell_exit_fd_;
}
}
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 87cca29..ab11bdd 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -58,10 +58,6 @@
#define cpu_to_le16(x) htole16(x)
#define cpu_to_le32(x) htole32(x)
-#define FUNCTIONFS_ENDPOINT_ALLOC _IOR('g', 231, __u32)
-
-static constexpr size_t ENDPOINT_ALLOC_RETRIES = 10;
-
static int dummy_fd = -1;
struct func_desc {
@@ -256,7 +252,6 @@
ssize_t ret;
struct desc_v1 v1_descriptor;
struct desc_v2 v2_descriptor;
- size_t retries = 0;
v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
@@ -326,30 +321,6 @@
h->read_aiob.fd = h->bulk_out;
h->write_aiob.fd = h->bulk_in;
-
- h->max_rw = MAX_PAYLOAD;
- while (h->max_rw >= USB_FFS_BULK_SIZE && retries < ENDPOINT_ALLOC_RETRIES) {
- int ret_in = ioctl(h->bulk_in, FUNCTIONFS_ENDPOINT_ALLOC, static_cast<__u32>(h->max_rw));
- int errno_in = errno;
- int ret_out = ioctl(h->bulk_out, FUNCTIONFS_ENDPOINT_ALLOC, static_cast<__u32>(h->max_rw));
- int errno_out = errno;
-
- if (ret_in || ret_out) {
- if (errno_in == ENODEV || errno_out == ENODEV) {
- std::this_thread::sleep_for(100ms);
- retries += 1;
- continue;
- }
- h->max_rw /= 2;
- } else {
- return true;
- }
- }
-
- D("[ adb: cannot call endpoint alloc: errno=%d ]", errno);
- // Kernel pre-allocation could have failed for recoverable reasons.
- // Continue running with a safe max rw size.
- h->max_rw = USB_FFS_BULK_SIZE;
return true;
err:
@@ -403,7 +374,7 @@
const char* buf = static_cast<const char*>(data);
while (len > 0) {
- int write_len = std::min(h->max_rw, len);
+ int write_len = std::min(USB_FFS_BULK_SIZE, len);
int n = adb_write(h->bulk_in, buf, write_len);
if (n < 0) {
D("ERROR: fd = %d, n = %d: %s", h->bulk_in, n, strerror(errno));
@@ -422,7 +393,7 @@
char* buf = static_cast<char*>(data);
while (len > 0) {
- int read_len = std::min(h->max_rw, len);
+ int read_len = std::min(USB_FFS_BULK_SIZE, len);
int n = adb_read(h->bulk_out, buf, read_len);
if (n < 0) {
D("ERROR: fd = %d, n = %d: %s", h->bulk_out, n, strerror(errno));
diff --git a/adb/daemon/usb.h b/adb/daemon/usb.h
index db1a6d6..15a7f65 100644
--- a/adb/daemon/usb.h
+++ b/adb/daemon/usb.h
@@ -54,7 +54,5 @@
// read and write threads.
struct aio_block read_aiob;
struct aio_block write_aiob;
-
- int max_rw;
};
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index d285561..9776c1b 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -21,6 +21,7 @@
#include "fdevent.h"
#include <fcntl.h>
+#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@@ -36,19 +37,13 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
+#include <android-base/threads.h>
#include "adb_io.h"
#include "adb_trace.h"
#include "adb_unique_fd.h"
#include "adb_utils.h"
-#if !ADB_HOST
-// This socket is used when a subproc shell service exists.
-// It wakes up the fdevent_loop() and cause the correct handling
-// of the shell's pseudo-tty master. I.e. force close it.
-int SHELL_EXIT_NOTIFY_FD = -1;
-#endif // !ADB_HOST
-
#define FDE_EVENTMASK 0x00ff
#define FDE_STATEMASK 0xff00
@@ -78,7 +73,7 @@
static auto& g_pending_list = *new std::list<fdevent*>();
static std::atomic<bool> terminate_loop(false);
static bool main_thread_valid;
-static unsigned long main_thread_id;
+static uint64_t main_thread_id;
static auto& run_queue_notify_fd = *new unique_fd();
static auto& run_queue_mutex = *new std::mutex();
@@ -86,13 +81,13 @@
void check_main_thread() {
if (main_thread_valid) {
- CHECK_EQ(main_thread_id, adb_thread_id());
+ CHECK_EQ(main_thread_id, android::base::GetThreadId());
}
}
void set_main_thread() {
main_thread_valid = true;
- main_thread_id = adb_thread_id();
+ main_thread_id = android::base::GetThreadId();
}
static std::string dump_fde(const fdevent* fde) {
@@ -294,72 +289,6 @@
fde->func(fde->fd, events, fde->arg);
}
-#if !ADB_HOST
-
-#include <sys/ioctl.h>
-
-static void fdevent_subproc_event_func(int fd, unsigned ev, void* /* userdata */) {
- D("subproc handling on fd = %d, ev = %x", fd, ev);
-
- CHECK_GE(fd, 0);
-
- if (ev & FDE_READ) {
- int subproc_fd;
-
- if(!ReadFdExactly(fd, &subproc_fd, sizeof(subproc_fd))) {
- LOG(FATAL) << "Failed to read the subproc's fd from " << fd;
- }
- auto it = g_poll_node_map.find(subproc_fd);
- if (it == g_poll_node_map.end()) {
- D("subproc_fd %d cleared from fd_table", subproc_fd);
- adb_close(subproc_fd);
- return;
- }
- fdevent* subproc_fde = it->second.fde;
- if(subproc_fde->fd != subproc_fd) {
- // Already reallocated?
- LOG(FATAL) << "subproc_fd(" << subproc_fd << ") != subproc_fde->fd(" << subproc_fde->fd
- << ")";
- return;
- }
-
- subproc_fde->force_eof = 1;
-
- int rcount = 0;
- ioctl(subproc_fd, FIONREAD, &rcount);
- D("subproc with fd %d has rcount=%d, err=%d", subproc_fd, rcount, errno);
- if (rcount != 0) {
- // If there is data left, it will show up in the select().
- // This works because there is no other thread reading that
- // data when in this fd_func().
- return;
- }
-
- D("subproc_fde %s", dump_fde(subproc_fde).c_str());
- subproc_fde->events |= FDE_READ;
- if(subproc_fde->state & FDE_PENDING) {
- return;
- }
- subproc_fde->state |= FDE_PENDING;
- fdevent_call_fdfunc(subproc_fde);
- }
-}
-
-static void fdevent_subproc_setup() {
- int s[2];
-
- if(adb_socketpair(s)) {
- PLOG(FATAL) << "cannot create shell-exit socket-pair";
- }
- D("fdevent_subproc: socket pair (%d, %d)", s[0], s[1]);
-
- SHELL_EXIT_NOTIFY_FD = s[0];
- fdevent *fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL);
- CHECK(fde != nullptr) << "cannot create fdevent for shell-exit handler";
- fdevent_add(fde, FDE_READ);
-}
-#endif // !ADB_HOST
-
static void fdevent_run_flush() EXCLUDES(run_queue_mutex) {
// We need to be careful around reentrancy here, since a function we call can queue up another
// function.
@@ -400,6 +329,10 @@
PLOG(FATAL) << "failed to create run queue notify socketpair";
}
+ if (!set_file_block_mode(s[0], false) || !set_file_block_mode(s[1], false)) {
+ PLOG(FATAL) << "failed to make run queue notify socket nonblocking";
+ }
+
run_queue_notify_fd.reset(s[0]);
fdevent* fde = fdevent_create(s[1], fdevent_run_func, nullptr);
CHECK(fde != nullptr);
@@ -416,7 +349,12 @@
// run_queue_notify_fd could still be -1 if we're called before fdevent has finished setting up.
// In that case, rely on the setup code to flush the queue without a notification being needed.
if (run_queue_notify_fd != -1) {
- if (adb_write(run_queue_notify_fd.get(), "", 1) != 1) {
+ int rc = adb_write(run_queue_notify_fd.get(), "", 1);
+
+ // It's possible that we get EAGAIN here, if lots of notifications came in while handling.
+ if (rc == 0) {
+ PLOG(FATAL) << "run queue notify fd was closed?";
+ } else if (rc == -1 && errno != EAGAIN) {
PLOG(FATAL) << "failed to write to run queue notify fd";
}
}
@@ -424,9 +362,6 @@
void fdevent_loop() {
set_main_thread();
-#if !ADB_HOST
- fdevent_subproc_setup();
-#endif // !ADB_HOST
fdevent_run_setup();
while (true) {
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index 63cc4d1..95dc4c2 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -99,7 +99,7 @@
std::vector<std::unique_ptr<FdHandler>> fd_handlers;
for (size_t i = 0; i < read_fds.size(); ++i) {
- fd_handlers.push_back(std::unique_ptr<FdHandler>(new FdHandler(read_fds[i], write_fds[i])));
+ fd_handlers.push_back(std::make_unique<FdHandler>(read_fds[i], write_fds[i]));
}
fdevent_loop();
@@ -180,7 +180,13 @@
PrepareThread();
std::thread thread(fdevent_loop);
- for (int i = 0; i < 100; ++i) {
+ // Block the main thread for a long time while we queue our callbacks.
+ fdevent_run_on_main_thread([]() {
+ check_main_thread();
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ });
+
+ for (int i = 0; i < 1000000; ++i) {
fdevent_run_on_main_thread([i, &vec]() {
check_main_thread();
vec.push_back(i);
@@ -189,8 +195,8 @@
TerminateThread(thread);
- ASSERT_EQ(100u, vec.size());
- for (int i = 0; i < 100; ++i) {
+ ASSERT_EQ(1000000u, vec.size());
+ for (int i = 0; i < 1000000; ++i) {
ASSERT_EQ(i, vec[i]);
}
}
diff --git a/adb/fdevent_test.h b/adb/fdevent_test.h
index 5ca49ac..1a2d41c 100644
--- a/adb/fdevent_test.h
+++ b/adb/fdevent_test.h
@@ -52,13 +52,8 @@
}
size_t GetAdditionalLocalSocketCount() {
-#if ADB_HOST
// dummy socket installed in PrepareThread() + fdevent_run_on_main_thread socket
return 2;
-#else
- // dummy socket + fdevent_run_on_main_thread + fdevent_subproc_setup() sockets
- return 3;
-#endif
}
void TerminateThread(std::thread& thread) {
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index 04ad6f3..6b40056 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -113,16 +113,12 @@
asocket* s = create_local_socket(arg->socket_fd);
ASSERT_TRUE(s != nullptr);
arg->bytes_written = 0;
- while (true) {
- std::string data;
- data.resize(MAX_PAYLOAD);
- arg->bytes_written += data.size();
- int ret = s->enqueue(s, std::move(data));
- if (ret == 1) {
- // The writer has one packet waiting to send.
- break;
- }
- }
+
+ std::string data;
+ data.resize(MAX_PAYLOAD);
+ arg->bytes_written += data.size();
+ int ret = s->enqueue(s, std::move(data));
+ ASSERT_EQ(1, ret);
asocket* cause_close_s = create_local_socket(arg->cause_close_fd);
ASSERT_TRUE(cause_close_s != nullptr);
@@ -213,6 +209,45 @@
TerminateThread(thread);
}
+// Ensure that if we fail to write output to an fd, we will still flush data coming from it.
+TEST_F(LocalSocketTest, flush_after_shutdown) {
+ int head_fd[2];
+ int tail_fd[2];
+ ASSERT_EQ(0, adb_socketpair(head_fd));
+ ASSERT_EQ(0, adb_socketpair(tail_fd));
+
+ asocket* head = create_local_socket(head_fd[1]);
+ asocket* tail = create_local_socket(tail_fd[1]);
+
+ head->peer = tail;
+ head->ready(head);
+
+ tail->peer = head;
+ tail->ready(tail);
+
+ PrepareThread();
+ std::thread thread(fdevent_loop);
+
+ EXPECT_TRUE(WriteFdExactly(head_fd[0], "foo", 3));
+
+ EXPECT_EQ(0, adb_shutdown(head_fd[0], SHUT_RD));
+ const char* str = "write succeeds, but local_socket will fail to write";
+ EXPECT_TRUE(WriteFdExactly(tail_fd[0], str, strlen(str)));
+ EXPECT_TRUE(WriteFdExactly(head_fd[0], "bar", 3));
+
+ char buf[6];
+ EXPECT_TRUE(ReadFdExactly(tail_fd[0], buf, 6));
+ EXPECT_EQ(0, memcmp(buf, "foobar", 6));
+
+ adb_close(head_fd[0]);
+ adb_close(tail_fd[0]);
+
+ // Wait until the local sockets are closed.
+ std::this_thread::sleep_for(SLEEP_FOR_FDEVENT);
+ ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
+ TerminateThread(thread);
+}
+
#if defined(__linux__)
static void ClientThreadFunc() {
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 0007fed..e05a3db 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -106,50 +106,131 @@
}
}
+enum class SocketFlushResult {
+ Destroyed,
+ TryAgain,
+ Completed,
+};
+
+static SocketFlushResult local_socket_flush_incoming(asocket* s) {
+ while (!s->packet_queue.empty()) {
+ Range& r = s->packet_queue.front();
+
+ int rc = adb_write(s->fd, r.data(), r.size());
+ if (rc == static_cast<int>(r.size())) {
+ s->packet_queue.pop_front();
+ } else if (rc > 0) {
+ r.drop_front(rc);
+ fdevent_add(&s->fde, FDE_WRITE);
+ return SocketFlushResult::TryAgain;
+ } else if (rc == -1 && errno == EAGAIN) {
+ fdevent_add(&s->fde, FDE_WRITE);
+ return SocketFlushResult::TryAgain;
+ }
+
+ // We failed to write, but it's possible that we can still read from the socket.
+ // Give that a try before giving up.
+ s->has_write_error = true;
+ break;
+ }
+
+ // If we sent the last packet of a closing socket, we can now destroy it.
+ if (s->closing) {
+ s->close(s);
+ return SocketFlushResult::Destroyed;
+ }
+
+ fdevent_del(&s->fde, FDE_WRITE);
+ return SocketFlushResult::Completed;
+}
+
+// Returns false if the socket has been closed and destroyed as a side-effect of this function.
+static bool local_socket_flush_outgoing(asocket* s) {
+ const size_t max_payload = s->get_max_payload();
+ std::string data;
+ data.resize(max_payload);
+ char* x = &data[0];
+ size_t avail = max_payload;
+ int r = 0;
+ int is_eof = 0;
+
+ while (avail > 0) {
+ r = adb_read(s->fd, x, avail);
+ D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%zu", s->id, s->fd, r,
+ r < 0 ? errno : 0, avail);
+ if (r == -1) {
+ if (errno == EAGAIN) {
+ break;
+ }
+ } else if (r > 0) {
+ avail -= r;
+ x += r;
+ continue;
+ }
+
+ /* r = 0 or unhandled error */
+ is_eof = 1;
+ break;
+ }
+ D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d", s->id, s->fd, r, is_eof,
+ s->fde.force_eof);
+
+ if (avail != max_payload && s->peer) {
+ data.resize(max_payload - avail);
+
+ // s->peer->enqueue() may call s->close() and free s,
+ // so save variables for debug printing below.
+ unsigned saved_id = s->id;
+ int saved_fd = s->fd;
+ r = s->peer->enqueue(s->peer, std::move(data));
+ D("LS(%u): fd=%d post peer->enqueue(). r=%d", saved_id, saved_fd, r);
+
+ if (r < 0) {
+ // Error return means they closed us as a side-effect and we must
+ // return immediately.
+ //
+ // Note that if we still have buffered packets, the socket will be
+ // placed on the closing socket list. This handler function will be
+ // called again to process FDE_WRITE events.
+ return false;
+ }
+
+ if (r > 0) {
+ /* if the remote cannot accept further events,
+ ** we disable notification of READs. They'll
+ ** be enabled again when we get a call to ready()
+ */
+ fdevent_del(&s->fde, FDE_READ);
+ }
+ }
+
+ // Don't allow a forced eof if data is still there.
+ if ((s->fde.force_eof && !r) || is_eof) {
+ D(" closing because is_eof=%d r=%d s->fde.force_eof=%d", is_eof, r, s->fde.force_eof);
+ s->close(s);
+ return false;
+ }
+
+ return true;
+}
+
static int local_socket_enqueue(asocket* s, std::string data) {
D("LS(%d): enqueue %zu", s->id, data.size());
Range r(std::move(data));
-
- /* if there is already data queue'd, we will receive
- ** events when it's time to write. just add this to
- ** the tail
- */
- if (!s->packet_queue.empty()) {
- goto enqueue;
- }
-
- /* write as much as we can, until we
- ** would block or there is an error/eof
- */
- while (!r.empty()) {
- int rc = adb_write(s->fd, r.data(), r.size());
- if (rc > 0) {
- r.drop_front(rc);
- continue;
- }
-
- if (rc == 0 || errno != EAGAIN) {
- D("LS(%d): not ready, errno=%d: %s", s->id, errno, strerror(errno));
- s->has_write_error = true;
- s->close(s);
- return 1; /* not ready (error) */
- } else {
- // errno == EAGAIN
- break;
- }
- }
-
- if (r.empty()) {
- return 0; /* ready for more data */
- }
-
-enqueue:
- /* make sure we are notified when we can drain the queue */
s->packet_queue.push_back(std::move(r));
- fdevent_add(&s->fde, FDE_WRITE);
+ switch (local_socket_flush_incoming(s)) {
+ case SocketFlushResult::Destroyed:
+ return -1;
- return 1; /* not ready (backlog) */
+ case SocketFlushResult::TryAgain:
+ return 1;
+
+ case SocketFlushResult::Completed:
+ return 0;
+ }
+
+ return !s->packet_queue.empty();
}
static void local_socket_ready(asocket* s) {
@@ -224,114 +305,21 @@
** in order to simplify the code.
*/
if (ev & FDE_WRITE) {
- while (!s->packet_queue.empty()) {
- Range& r = s->packet_queue.front();
- while (!r.empty()) {
- int rc = adb_write(fd, r.data(), r.size());
- if (rc == -1) {
- /* returning here is ok because FDE_READ will
- ** be processed in the next iteration loop
- */
- if (errno == EAGAIN) {
- return;
- }
- } else if (rc > 0) {
- r.drop_front(rc);
- continue;
- }
-
- D(" closing after write because rc=%d and errno is %d", rc, errno);
- s->has_write_error = true;
- s->close(s);
+ switch (local_socket_flush_incoming(s)) {
+ case SocketFlushResult::Destroyed:
return;
- }
- if (r.empty()) {
- s->packet_queue.pop_front();
- }
+ case SocketFlushResult::TryAgain:
+ break;
+
+ case SocketFlushResult::Completed:
+ s->peer->ready(s->peer);
+ break;
}
-
- /* if we sent the last packet of a closing socket,
- ** we can now destroy it.
- */
- if (s->closing) {
- D(" closing because 'closing' is set after write");
- s->close(s);
- return;
- }
-
- /* no more packets queued, so we can ignore
- ** writable events again and tell our peer
- ** to resume writing
- */
- fdevent_del(&s->fde, FDE_WRITE);
- s->peer->ready(s->peer);
}
if (ev & FDE_READ) {
- const size_t max_payload = s->get_max_payload();
- std::string data;
- data.resize(max_payload);
- char* x = &data[0];
- size_t avail = max_payload;
- int r = 0;
- int is_eof = 0;
-
- while (avail > 0) {
- r = adb_read(fd, x, avail);
- D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%zu", s->id, s->fd, r,
- r < 0 ? errno : 0, avail);
- if (r == -1) {
- if (errno == EAGAIN) {
- break;
- }
- } else if (r > 0) {
- avail -= r;
- x += r;
- continue;
- }
-
- /* r = 0 or unhandled error */
- is_eof = 1;
- break;
- }
- D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d", s->id, s->fd, r, is_eof,
- s->fde.force_eof);
-
- if (avail != max_payload && s->peer) {
- data.resize(max_payload - avail);
-
- // s->peer->enqueue() may call s->close() and free s,
- // so save variables for debug printing below.
- unsigned saved_id = s->id;
- int saved_fd = s->fd;
- r = s->peer->enqueue(s->peer, std::move(data));
- D("LS(%u): fd=%d post peer->enqueue(). r=%d", saved_id, saved_fd, r);
-
- if (r < 0) {
- /* error return means they closed us as a side-effect
- ** and we must return immediately.
- **
- ** note that if we still have buffered packets, the
- ** socket will be placed on the closing socket list.
- ** this handler function will be called again
- ** to process FDE_WRITE events.
- */
- return;
- }
-
- if (r > 0) {
- /* if the remote cannot accept further events,
- ** we disable notification of READs. They'll
- ** be enabled again when we get a call to ready()
- */
- fdevent_del(&s->fde, FDE_READ);
- }
- }
- /* Don't allow a forced eof if data is still there */
- if ((s->fde.force_eof && !r) || is_eof) {
- D(" closing because is_eof=%d r=%d s->fde.force_eof=%d", is_eof, r, s->fde.force_eof);
- s->close(s);
+ if (!local_socket_flush_outgoing(s)) {
return;
}
}
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 307be6d..fd08ad8 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -92,11 +92,6 @@
return 0;
}
-static __inline__ unsigned long adb_thread_id()
-{
- return GetCurrentThreadId();
-}
-
static __inline__ void close_on_exec(int fd)
{
/* nothing really */
@@ -625,11 +620,6 @@
return path[0] == '/';
}
-static __inline__ unsigned long adb_thread_id()
-{
- return (unsigned long)gettid();
-}
-
#endif /* !_WIN32 */
static inline void disable_tcp_nagle(int fd) {
diff --git a/adb/sysdeps/memory.h b/adb/sysdeps/memory.h
new file mode 100644
index 0000000..0e4c509
--- /dev/null
+++ b/adb/sysdeps/memory.h
@@ -0,0 +1,36 @@
+#pragma once
+
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+#include <type_traits>
+
+#if defined(_WIN32)
+// We don't have C++14 on Windows yet.
+// Reimplement std::make_unique ourselves until we do.
+
+namespace std {
+
+template <typename T, typename... Args>
+typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type make_unique(
+ Args&&... args) {
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+
+} // namespace std
+
+#endif
diff --git a/adb/sysdeps/posix/network.cpp b/adb/sysdeps/posix/network.cpp
index 45da5af..ecd1fd2 100644
--- a/adb/sysdeps/posix/network.cpp
+++ b/adb/sysdeps/posix/network.cpp
@@ -105,8 +105,7 @@
}
if (type == SOCK_STREAM || type == SOCK_SEQPACKET) {
- // Arbitrarily selected value, ported from libcutils.
- if (listen(s, 4) != 0) {
+ if (listen(s, SOMAXCONN) != 0) {
set_error(error);
return -1;
}
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 5873b2b..cd7d187 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -747,8 +747,6 @@
return fd;
}
-#define LISTEN_BACKLOG 4
-
// interface_address is INADDR_LOOPBACK or INADDR_ANY.
static int _network_server(int port, int type, u_long interface_address, std::string* error) {
struct sockaddr_in addr;
@@ -805,7 +803,7 @@
return -1;
}
if (type == SOCK_STREAM) {
- if (listen(s, LISTEN_BACKLOG) == SOCKET_ERROR) {
+ if (listen(s, SOMAXCONN) == SOCKET_ERROR) {
const DWORD err = WSAGetLastError();
*error = android::base::StringPrintf(
"cannot listen on socket: %s", android::base::SystemErrorCodeToString(err).c_str());
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 98c8a59..e771106 100644
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -217,8 +217,12 @@
ipv4.listen(1)
ipv6 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
- ipv6.bind(('::1', ipv4.getsockname()[1] + 1))
- ipv6.listen(1)
+ try:
+ ipv6.bind(('::1', ipv4.getsockname()[1] + 1))
+ ipv6.listen(1)
+ except socket.error:
+ print("IPv6 not available, skipping")
+ return
for s in (ipv4, ipv6):
port = s.getsockname()[1]
diff --git a/adb/test_device.py b/adb/test_device.py
index 4cf2206..d39eb14 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -31,6 +31,7 @@
import subprocess
import sys
import tempfile
+import threading
import time
import unittest
@@ -493,6 +494,29 @@
stdout, _ = self.device.shell(["cat", log_path])
self.assertEqual(stdout.strip(), "SIGHUP")
+ def test_exit_stress(self):
+ """Hammer `adb shell exit 42` with multiple threads."""
+ thread_count = 48
+ result = dict()
+ def hammer(thread_idx, thread_count, result):
+ success = True
+ for i in range(thread_idx, 240, thread_count):
+ ret = subprocess.call(['adb', 'shell', 'exit {}'.format(i)])
+ if ret != i % 256:
+ success = False
+ break
+ result[thread_idx] = success
+
+ threads = []
+ for i in range(thread_count):
+ thread = threading.Thread(target=hammer, args=(i, thread_count, result))
+ thread.start()
+ threads.append(thread)
+ for thread in threads:
+ thread.join()
+ for i, success in result.iteritems():
+ self.assertTrue(success)
+
class ArgumentEscapingTest(DeviceTest):
def test_shell_escaping(self):
@@ -1163,7 +1187,7 @@
# Verify that the device ended up with the expected UTF-8 path
output = self.device.shell(
['ls', '/data/local/tmp/adb-test-*'])[0].strip()
- self.assertEqual(remote_path.encode('utf-8'), output)
+ self.assertEqual(remote_path, output)
# pull.
self.device.pull(remote_path, tf.name)
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 3329f0f..37b56e2 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -17,6 +17,8 @@
#define TRACE_TAG TRANSPORT
#include "sysdeps.h"
+#include "sysdeps/memory.h"
+
#include "transport.h"
#include <ctype.h>
@@ -79,7 +81,7 @@
read_thread_ = std::thread([this]() {
LOG(INFO) << this->transport_name_ << ": read thread spawning";
while (true) {
- std::unique_ptr<apacket> packet(new apacket());
+ auto packet = std::make_unique<apacket>();
if (!underlying_->Read(packet.get())) {
PLOG(INFO) << this->transport_name_ << ": read failed";
break;
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index ff395dc..c09fcb7 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -45,6 +45,7 @@
#include "adb_unique_fd.h"
#include "adb_utils.h"
#include "sysdeps/chrono.h"
+#include "sysdeps/memory.h"
#if ADB_HOST
@@ -450,9 +451,8 @@
#if ADB_HOST
// Emulator connection.
if (local) {
- std::unique_ptr<BlockingConnection> emulator_connection(
- new EmulatorConnection(std::move(fd), adb_port));
- t->connection.reset(new BlockingConnectionAdapter(std::move(emulator_connection)));
+ auto emulator_connection = std::make_unique<EmulatorConnection>(std::move(fd), adb_port);
+ t->connection = std::make_unique<BlockingConnectionAdapter>(std::move(emulator_connection));
std::lock_guard<std::mutex> lock(local_transports_lock);
atransport* existing_transport = find_emulator_transport_by_adb_port_locked(adb_port);
if (existing_transport != NULL) {
@@ -471,7 +471,7 @@
#endif
// Regular tcp connection.
- std::unique_ptr<BlockingConnection> fd_connection(new FdConnection(std::move(fd)));
- t->connection.reset(new BlockingConnectionAdapter(std::move(fd_connection)));
+ auto fd_connection = std::make_unique<FdConnection>(std::move(fd));
+ t->connection = std::make_unique<BlockingConnectionAdapter>(std::move(fd_connection));
return fail;
}
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 33e00a1..e9a75cd 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -17,6 +17,7 @@
#define TRACE_TAG TRANSPORT
#include "sysdeps.h"
+#include "sysdeps/memory.h"
#include "transport.h"
#include <stdio.h>
@@ -174,8 +175,8 @@
void init_usb_transport(atransport* t, usb_handle* h) {
D("transport: usb");
- std::unique_ptr<BlockingConnection> connection(new UsbConnection(h));
- t->connection.reset(new BlockingConnectionAdapter(std::move(connection)));
+ auto connection = std::make_unique<UsbConnection>(h);
+ t->connection = std::make_unique<BlockingConnectionAdapter>(std::move(connection));
t->type = kTransportUsb;
}
diff --git a/base/Android.bp b/base/Android.bp
index 5d70d47..7b0ba11 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -50,6 +50,7 @@
"quick_exit.cpp",
"stringprintf.cpp",
"strings.cpp",
+ "threads.cpp",
"test_utils.cpp",
],
diff --git a/base/include/android-base/test_utils.h b/base/include/android-base/test_utils.h
index 2edafe3..b95fa07 100644
--- a/base/include/android-base/test_utils.h
+++ b/base/include/android-base/test_utils.h
@@ -31,6 +31,8 @@
// Release the ownership of fd, caller is reponsible for closing the
// fd or stream properly.
int release();
+ // Don't remove the temporary file in the destructor.
+ void DoNotRemove() { remove_file_ = false; }
int fd;
char path[1024];
@@ -38,6 +40,8 @@
private:
void init(const std::string& tmp_dir);
+ bool remove_file_ = true;
+
DISALLOW_COPY_AND_ASSIGN(TemporaryFile);
};
diff --git a/base/include/android-base/threads.h b/base/include/android-base/threads.h
new file mode 100644
index 0000000..85e65ba
--- /dev/null
+++ b/base/include/android-base/threads.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BASE_THREADS_H
+#define ANDROID_BASE_THREADS_H
+
+#include <stdint.h>
+
+namespace android {
+namespace base {
+uint64_t GetThreadId();
+}
+} // namespace android
+
+#endif
diff --git a/base/logging.cpp b/base/logging.cpp
index a31feef..30d7f8d 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -21,6 +21,7 @@
#include "android-base/logging.h"
#include <fcntl.h>
+#include <inttypes.h>
#include <libgen.h>
#include <time.h>
@@ -54,41 +55,7 @@
#include <android-base/macros.h>
#include <android-base/strings.h>
-
-// For gettid.
-#if defined(__APPLE__)
-#include "AvailabilityMacros.h" // For MAC_OS_X_VERSION_MAX_ALLOWED
-#include <stdint.h>
-#include <stdlib.h>
-#include <sys/syscall.h>
-#include <sys/time.h>
-#include <unistd.h>
-#elif defined(__linux__) && !defined(__ANDROID__)
-#include <syscall.h>
-#include <unistd.h>
-#elif defined(_WIN32)
-#include <windows.h>
-#endif
-
-#if defined(_WIN32)
-typedef uint32_t thread_id;
-#else
-typedef pid_t thread_id;
-#endif
-
-static thread_id GetThreadId() {
-#if defined(__BIONIC__)
- return gettid();
-#elif defined(__APPLE__)
- uint64_t tid;
- pthread_threadid_np(NULL, &tid);
- return tid;
-#elif defined(__linux__)
- return syscall(__NR_gettid);
-#elif defined(_WIN32)
- return GetCurrentThreadId();
-#endif
-}
+#include <android-base/threads.h>
namespace {
#if defined(__GLIBC__)
@@ -223,8 +190,8 @@
static_assert(arraysize(log_characters) - 1 == FATAL + 1,
"Mismatch in size of log_characters and values in LogSeverity");
char severity_char = log_characters[severity];
- fprintf(stderr, "%s %c %s %5d %5d %s:%u] %s\n", tag ? tag : "nullptr", severity_char, timestamp,
- getpid(), GetThreadId(), file, line, message);
+ fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n", tag ? tag : "nullptr", severity_char,
+ timestamp, getpid(), GetThreadId(), file, line, message);
}
void DefaultAborter(const char* abort_message) {
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index 9d8dfb2..1619c21 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -92,7 +92,9 @@
if (fd != -1) {
close(fd);
}
- unlink(path);
+ if (remove_file_) {
+ unlink(path);
+ }
}
int TemporaryFile::release() {
diff --git a/base/threads.cpp b/base/threads.cpp
new file mode 100644
index 0000000..a71382b
--- /dev/null
+++ b/base/threads.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/threads.h>
+
+#include <stdint.h>
+#include <unistd.h>
+
+#if defined(__APPLE__)
+#include <pthread.h>
+#elif defined(__linux__) && !defined(__ANDROID__)
+#include <syscall.h>
+#elif defined(_WIN32)
+#include <windows.h>
+#endif
+
+namespace android {
+namespace base {
+
+uint64_t GetThreadId() {
+#if defined(__BIONIC__)
+ return gettid();
+#elif defined(__APPLE__)
+ uint64_t tid;
+ pthread_threadid_np(NULL, &tid);
+ return tid;
+#elif defined(__linux__)
+ return syscall(__NR_gettid);
+#elif defined(_WIN32)
+ return GetCurrentThreadId();
+#endif
+}
+
+} // namespace base
+} // namespace android
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 79702a6..b194bbe 100755
--- a/bootstat/boot_reason_test.sh
+++ b/bootstat/boot_reason_test.sh
@@ -239,6 +239,8 @@
return 0
}
+BAD_BOOTLOADER_REASON=
+
[ "USAGE: EXPECT_PROPERTY <prop> <value> [--allow_failure]
Returns true (0) if current return (regex) value is true and the result matches
@@ -249,9 +251,20 @@
value="${2}"
shift 2
val=`adb shell getprop ${property} 2>&1`
- EXPECT_EQ "${value}" "${val}" for Android property ${property} ||
- [ -n "${1}" ] ||
- save_ret=${?}
+ EXPECT_EQ "${value}" "${val}" for Android property ${property}
+ local_ret=${?}
+ if [ 0 != ${local_ret} -a "ro.boot.bootreason" = "${property}" ]; then
+ if [ -z "${BAD_BOOTLOADER_REASON}" ]; then
+ BAD_BOOTLOADER_REASON=${val}
+ elif [ X"${BAD_BOOTLOADER_REASON}" = X"${val}" ]; then
+ local_ret=0
+ fi
+ fi
+ if [ 0 != ${local_ret} ]; then
+ if [ -z "${1}" ] ; then
+ save_ret=${local_ret}
+ fi
+ fi
return ${save_ret}
}
@@ -287,6 +300,7 @@
bootstat: Service started: /system/bin/bootstat --record_boot_reason
bootstat: Service started: /system/bin/bootstat --record_time_since_factory_reset
bootstat: Service started: /system/bin/bootstat -l
+bootstat: Service started: /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l
bootstat: Battery level at shutdown 100%
bootstat: Battery level at startup 100%
init : Parsing file /system/etc/init/bootstat.rc...
@@ -407,29 +421,31 @@
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,?*) ;;
+ 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,?*) ;;
+ 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" ;;
+ *wdog* | *watchdog* ) var="watchdog" ;;
+ *powerkey* | *power_key* | *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" ;;
+ *usb* ) var="cold,charger" ;;
+ *rtc* ) var="cold,rtc" ;;
+ *bootloader* ) var="bootloader" ;;
+ * ) var="reboot" ;;
esac
echo ${var}
}
@@ -792,6 +808,63 @@
exitPstore
}
+[ "USAGE: test_kernel_panic_subreason
+
+kernel_panic_subreason test:
+- echo SysRq : Trigger a crash : 'test' | adb shell su root tee /dev/kmsg
+- 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" ]
+test_kernel_panic_subreason() {
+ checkDebugBuild || return
+ duration_test ">90"
+ panic_msg="kernel_panic,sysrq,test"
+ enterPstore
+ if [ ${?} != 0 ]; then
+ echo " or functional bootloader" >&2
+ panic_msg="\(kernel_panic,sysrq,test\|kernel_panic\)"
+ pstore_ok=true
+ fi
+ echo "SysRq : Trigger a crash : 'test'" | adb shell su root tee /dev/kmsg
+ 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,test \
+ "-bootstat: Unknown boot reason: kernel_panic,sysrq,test"
+ exitPstore
+}
+
+[ "USAGE: test_kernel_panic_hung
+
+kernel_panic_hung test:
+- echo Kernel panic - not synching: hung_task: blocked tasks |
+ adb shell su root tee /dev/kmsg
+- adb reboot warm
+- (wait until screen is up, boot has completed)
+- adb shell getprop sys.boot.reason
+- NB: should report kernel_panic,hung" ]
+test_kernel_panic_hung() {
+ checkDebugBuild || return
+ duration_test
+ panic_msg="kernel_panic,hung"
+ enterPstore
+ if [ ${?} != 0 ]; then
+ echo " or functional bootloader" >&2
+ panic_msg="\(kernel_panic,hung\|reboot,hung\)"
+ pstore_ok=true
+ fi
+ echo "Kernel panic - not syncing: hung_task: blocked tasks" |
+ adb shell su root tee /dev/kmsg
+ adb reboot warm
+ wait_for_screen
+ EXPECT_PROPERTY sys.boot.reason ${panic_msg}
+ EXPECT_PROPERTY persist.sys.boot.reason ${panic_msg}
+ report_bootstat_logs kernel_panic,hung
+ exitPstore
+}
+
[ "USAGE: test_warm
warm test
@@ -1051,12 +1124,12 @@
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
+ kernel_panic kernel_panic_subreason kernel_panic_hung 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
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index a1fe6ed..e60e6be 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -29,7 +29,9 @@
#include <ctime>
#include <map>
#include <memory>
+#include <regex>
#include <string>
+#include <utility>
#include <vector>
#include <android-base/chrono_utils.h>
@@ -138,7 +140,7 @@
// values.
const std::map<std::string, int32_t> kBootReasonMap = {
{"empty", kEmptyBootReason},
- {"unknown", kUnknownBootReason},
+ {"__BOOTSTAT_UNKNOWN__", kUnknownBootReason},
{"normal", 2},
{"recovery", 3},
{"reboot", 4},
@@ -191,12 +193,14 @@
{"s3_wakeup", 51},
{"kernel_panic,sysrq", 52},
{"kernel_panic,NULL", 53},
+ {"kernel_panic,null", 53},
{"kernel_panic,BUG", 54},
+ {"kernel_panic,bug", 54},
{"bootloader", 55},
{"cold", 56},
{"hard", 57},
{"warm", 58},
- {"recovery", 59},
+ // {"recovery", 59}, // Duplicate of enum 3 above. Immediate reuse possible.
{"thermal-shutdown", 60},
{"shutdown,thermal", 61},
{"shutdown,battery", 62},
@@ -227,7 +231,7 @@
{"shutdown,thermal,battery", 87},
{"reboot,its_just_so_hard", 88}, // produced by boot_reason_test
{"reboot,Its Just So Hard", 89}, // produced by boot_reason_test
- {"usb", 90},
+ // {"usb", 90}, // Duplicate of enum 80 above. Immediate reuse possible.
{"charge", 91},
{"oem_tz_crash", 92},
{"uvlo", 93},
@@ -285,6 +289,19 @@
{"oem_sdi_err_fatal", 145},
{"pmic_watchdog", 146},
{"software_master", 147},
+ {"cold,charger", 148},
+ {"cold,rtc", 149},
+ {"cold,rtc,2sec", 150},
+ {"reboot,tool", 151},
+ {"reboot,wdt", 152},
+ {"reboot,unknown", 153},
+ {"kernel_panic,audit", 154},
+ {"kernel_panic,atomic", 155},
+ {"kernel_panic,hung", 156},
+ {"kernel_panic,hung,rcu", 157},
+ {"kernel_panic,init", 158},
+ {"kernel_panic,oom", 159},
+ {"kernel_panic,stack", 160},
};
// Converts a string value representing the reason the system booted to an
@@ -461,11 +478,13 @@
}
return std::string::npos;
}
+
+ operator const std::string&() const { return console; }
};
// If bit error match to needle, correct it.
// Return true if any corrections were discovered and applied.
-bool correctForBer(std::string& reason, const std::string& needle) {
+bool correctForBitError(std::string& reason, const std::string& needle) {
bool corrected = false;
if (reason.length() < needle.length()) return corrected;
const pstoreConsole console(reason);
@@ -483,20 +502,215 @@
return corrected;
}
+// If bit error match to needle, correct it.
+// Return true if any corrections were discovered and applied.
+// Try again if we can replace underline with spaces.
+bool correctForBitErrorOrUnderline(std::string& reason, const std::string& needle) {
+ bool corrected = correctForBitError(reason, needle);
+ std::string _needle(needle);
+ std::transform(_needle.begin(), _needle.end(), _needle.begin(),
+ [](char c) { return (c == '_') ? ' ' : c; });
+ if (needle != _needle) {
+ corrected |= correctForBitError(reason, _needle);
+ }
+ return corrected;
+}
+
+// Converts a string value representing the reason the system booted to a
+// string complying with Android system standard reason.
+void transformReason(std::string& reason) {
+ std::transform(reason.begin(), reason.end(), reason.begin(), ::tolower);
+ std::transform(reason.begin(), reason.end(), reason.begin(),
+ [](char c) { return ::isblank(c) ? '_' : c; });
+ std::transform(reason.begin(), reason.end(), reason.begin(),
+ [](char c) { return ::isprint(c) ? c : '?'; });
+}
+
+// Check subreasons for reboot,<subreason> kernel_panic,sysrq,<subreason> or
+// kernel_panic,<subreason>.
+//
+// If quoted flag is set, pull out and correct single quoted ('), newline (\n)
+// or unprintable character terminated subreason, pos is supplied just beyond
+// first quote. if quoted false, pull out and correct newline (\n) or
+// unprintable character terminated subreason.
+//
+// Heuristics to find termination is painted into a corner:
+
+// single bit error for quote ' that we can block. It is acceptable for
+// the others 7, g in reason. 2/9 chance will miss the terminating quote,
+// but there is always the terminating newline that usually immediately
+// follows to fortify our chances.
+bool likely_single_quote(char c) {
+ switch (static_cast<uint8_t>(c)) {
+ case '\'': // '\''
+ case '\'' ^ 0x01: // '&'
+ case '\'' ^ 0x02: // '%'
+ case '\'' ^ 0x04: // '#'
+ case '\'' ^ 0x08: // '/'
+ return true;
+ case '\'' ^ 0x10: // '7'
+ break;
+ case '\'' ^ 0x20: // '\a' (unprintable)
+ return true;
+ case '\'' ^ 0x40: // 'g'
+ break;
+ case '\'' ^ 0x80: // 0xA7 (unprintable)
+ return true;
+ }
+ return false;
+}
+
+// ::isprint(c) and likely_space() will prevent us from being called for
+// fundamentally printable entries, except for '\r' and '\b'.
+//
+// Except for * and J, single bit errors for \n, all others are non-
+// printable so easy catch. It is _acceptable_ for *, J or j to exist in
+// the reason string, so 2/9 chance we will miss the terminating newline.
+//
+// NB: J might not be acceptable, except if at the beginning or preceded
+// with a space, '(' or any of the quotes and their BER aliases.
+// NB: * might not be acceptable, except if at the beginning or preceded
+// with a space, another *, or any of the quotes or their BER aliases.
+//
+// To reduce the chances to closer to 1/9 is too complicated for the gain.
+bool likely_newline(char c) {
+ switch (static_cast<uint8_t>(c)) {
+ case '\n': // '\n' (unprintable)
+ case '\n' ^ 0x01: // '\r' (unprintable)
+ case '\n' ^ 0x02: // '\b' (unprintable)
+ case '\n' ^ 0x04: // 0x0E (unprintable)
+ case '\n' ^ 0x08: // 0x02 (unprintable)
+ case '\n' ^ 0x10: // 0x1A (unprintable)
+ return true;
+ case '\n' ^ 0x20: // '*'
+ case '\n' ^ 0x40: // 'J'
+ break;
+ case '\n' ^ 0x80: // 0x8A (unprintable)
+ return true;
+ }
+ return false;
+}
+
+// ::isprint(c) will prevent us from being called for all the printable
+// matches below. If we let unprintables through because of this, they
+// get converted to underscore (_) by the validation phase.
+bool likely_space(char c) {
+ switch (static_cast<uint8_t>(c)) {
+ case ' ': // ' '
+ case ' ' ^ 0x01: // '!'
+ case ' ' ^ 0x02: // '"'
+ case ' ' ^ 0x04: // '$'
+ case ' ' ^ 0x08: // '('
+ case ' ' ^ 0x10: // '0'
+ case ' ' ^ 0x20: // '\0' (unprintable)
+ case ' ' ^ 0x40: // 'P'
+ case ' ' ^ 0x80: // 0xA0 (unprintable)
+ case '\t': // '\t'
+ case '\t' ^ 0x01: // '\b' (unprintable) (likely_newline counters)
+ case '\t' ^ 0x02: // '\v' (unprintable)
+ case '\t' ^ 0x04: // '\r' (unprintable) (likely_newline counters)
+ case '\t' ^ 0x08: // 0x01 (unprintable)
+ case '\t' ^ 0x10: // 0x19 (unprintable)
+ case '\t' ^ 0x20: // ')'
+ case '\t' ^ 0x40: // '1'
+ case '\t' ^ 0x80: // 0x89 (unprintable)
+ return true;
+ }
+ return false;
+}
+
+std::string getSubreason(const std::string& content, size_t pos, bool quoted) {
+ static constexpr size_t max_reason_length = 256;
+
+ std::string subReason(content.substr(pos, max_reason_length));
+ // Correct against any known strings that Bit Error Match
+ for (const auto& s : knownReasons) {
+ correctForBitErrorOrUnderline(subReason, s);
+ }
+ std::string terminator(quoted ? "'" : "");
+ for (const auto& m : kBootReasonMap) {
+ if (m.first.length() <= strlen("cold")) continue; // too short?
+ if (correctForBitErrorOrUnderline(subReason, m.first + terminator)) continue;
+ if (m.first.length() <= strlen("reboot,cold")) continue; // short?
+ if (android::base::StartsWith(m.first, "reboot,")) {
+ correctForBitErrorOrUnderline(subReason, m.first.substr(strlen("reboot,")) + terminator);
+ } else if (android::base::StartsWith(m.first, "kernel_panic,sysrq,")) {
+ correctForBitErrorOrUnderline(subReason,
+ m.first.substr(strlen("kernel_panic,sysrq,")) + terminator);
+ } else if (android::base::StartsWith(m.first, "kernel_panic,")) {
+ correctForBitErrorOrUnderline(subReason, m.first.substr(strlen("kernel_panic,")) + terminator);
+ }
+ }
+ for (pos = 0; pos < subReason.length(); ++pos) {
+ char c = subReason[pos];
+ if (!(::isprint(c) || likely_space(c)) || likely_newline(c) ||
+ (quoted && likely_single_quote(c))) {
+ subReason.erase(pos);
+ break;
+ }
+ }
+ transformReason(subReason);
+ return subReason;
+}
+
bool addKernelPanicSubReason(const pstoreConsole& 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.
+ if ((console.rfind("SysRq : Trigger a crash") != std::string::npos) ||
+ (console.rfind("PC is at sysrq_handle_crash+") != std::string::npos)) {
ret = "kernel_panic,sysrq";
+ // Invented for Android to allow daemons that specifically trigger sysrq
+ // to communicate more accurate boot subreasons via last console messages.
+ static constexpr char sysrqSubreason[] = "SysRq : Trigger a crash : '";
+ auto pos = console.rfind(sysrqSubreason);
+ if (pos != std::string::npos) {
+ ret += "," + getSubreason(console, pos + strlen(sysrqSubreason), /* quoted */ true);
+ }
return true;
}
if (console.rfind("Unable to handle kernel NULL pointer dereference at virtual address") !=
std::string::npos) {
- ret = "kernel_panic,NULL";
+ ret = "kernel_panic,null";
return true;
}
if (console.rfind("Kernel BUG at ") != std::string::npos) {
- ret = "kernel_panic,BUG";
+ ret = "kernel_panic,bug";
+ return true;
+ }
+
+ std::string panic("Kernel panic - not syncing: ");
+ auto pos = console.rfind(panic);
+ if (pos != std::string::npos) {
+ static const std::vector<std::pair<const std::string, const std::string>> panicReasons = {
+ {"Out of memory", "oom"},
+ {"out of memory", "oom"},
+ {"Oh boy, that early out of memory", "oom"}, // omg
+ {"BUG!", "bug"},
+ {"hung_task: blocked tasks", "hung"},
+ {"audit: ", "audit"},
+ {"scheduling while atomic", "atomic"},
+ {"Attempted to kill init!", "init"},
+ {"Requested init", "init"},
+ {"No working init", "init"},
+ {"Could not decompress init", "init"},
+ {"RCU Stall", "hung,rcu"},
+ {"stack-protector", "stack"},
+ {"kernel stack overflow", "stack"},
+ {"Corrupt kernel stack", "stack"},
+ {"low stack detected", "stack"},
+ {"corrupted stack end", "stack"},
+ };
+
+ ret = "kernel_panic";
+ for (auto& s : panicReasons) {
+ if (console.find(panic + s.first, pos) != std::string::npos) {
+ ret += "," + s.second;
+ return true;
+ }
+ }
+ auto reason = getSubreason(console, pos + panic.length(), /* newline */ false);
+ if (reason.length() > 3) {
+ ret += "," + reason;
+ }
return true;
}
return false;
@@ -506,32 +720,12 @@
return addKernelPanicSubReason(pstoreConsole(content), ret);
}
-// 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 : '?';
-}
-
-// Cleanup boot_reason regarding acceptable character set
-void transformReason(std::string& reason) {
- 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);
-}
-
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
@@ -566,26 +760,36 @@
// 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.
+ // to behave and follow our CTS standards.
+ //
+ // first member is the output
+ // second member is an unanchored regex for an alias
+ //
+ // If output has a prefix of <bang> '!', we do not use it as a
+ // match needle (and drop the <bang> prefix when landing in output),
+ // otherwise look for it as well. This helps keep the scale of the
+ // following table smaller.
static const std::vector<std::pair<const std::string, const std::string>> aliasReasons = {
{"watchdog", "wdog"},
- {"cold,powerkey", "powerkey"},
+ {"cold,powerkey", "powerkey|power_key|PowerKey"},
{"kernel_panic", "panic"},
{"shutdown,thermal", "thermal"},
{"warm,s3_wakeup", "s3_wakeup"},
{"hard,hw_reset", "hw_reset"},
+ {"cold,charger", "usb"},
+ {"cold,rtc", "rtc"},
{"reboot,2sec", "2sec_reboot"},
{"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) {
+ size_t firstHasNot = s.first[0] == '!';
+ if (!firstHasNot && (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;
+ if (s.second.size() && std::regex_search(reason, std::regex(s.second))) {
+ ret = s.first.substr(firstHasNot);
break;
}
}
@@ -624,28 +828,7 @@
static const char cmd[] = "reboot: Restarting system with command '";
size_t pos = console.rfind(cmd);
if (pos != std::string::npos) {
- pos += strlen(cmd);
- std::string subReason(content.substr(pos, max_reason_length));
- // Correct against any known strings that Bit Error Match
- for (const auto& s : knownReasons) {
- correctForBer(subReason, s);
- }
- for (const auto& m : kBootReasonMap) {
- if (m.first.length() <= strlen("cold")) continue; // too short?
- if (correctForBer(subReason, m.first + "'")) continue;
- if (m.first.length() <= strlen("reboot,cold")) continue; // short?
- if (!android::base::StartsWith(m.first, "reboot,")) continue;
- correctForBer(subReason, m.first.substr(strlen("reboot,")) + "'");
- }
- for (pos = 0; pos < subReason.length(); ++pos) {
- char c = subReason[pos];
- // #, &, %, / are common single bit error for ' that we can block
- if (!::isprint(c) || (c == '\'') || (c == '#') || (c == '&') || (c == '%') || (c == '/')) {
- subReason.erase(pos);
- break;
- }
- }
- transformReason(subReason);
+ std::string subReason(getSubreason(content, pos + strlen(cmd), /* quoted */ true));
if (subReason != "") { // Will not land "reboot" as that is too blunt.
if (isKernelRebootReason(subReason)) {
ret = "reboot," + subReason; // User space can't talk kernel reasons.
@@ -683,7 +866,7 @@
if (pos != std::string::npos) {
digits = content.substr(pos + strlen(battery), strlen("100 "));
// correct common errors
- correctForBer(digits, "100 ");
+ correctForBitError(digits, "100 ");
if (digits[0] == '!') digits[0] = '1';
if (digits[1] == '!') digits[1] = '1';
}
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 6b14289..6c8fecf 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -98,9 +98,6 @@
ifeq ($(strip $(BOARD_CHARGER_NO_UI)),true)
LOCAL_CHARGER_NO_UI := true
endif
-ifdef BRILLO
-LOCAL_CHARGER_NO_UI := true
-endif
LOCAL_SRC_FILES := \
healthd_common.cpp \
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 03ed55a..6f6e39f 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -20,7 +20,6 @@
#include <fcntl.h>
#include <linux/fs.h>
#include <mntent.h>
-#include <semaphore.h>
#include <sys/capability.h>
#include <sys/cdefs.h>
#include <sys/ioctl.h>
@@ -330,9 +329,39 @@
return stat;
}
-void RebootThread(unsigned int cmd, std::chrono::milliseconds shutdown_timeout, bool runFsck,
- sem_t* reboot_semaphore) {
+void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
+ bool runFsck) {
Timer t;
+ LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
+
+ // Ensure last reboot reason is reduced to canonical
+ // alias reported in bootloader or system boot reason.
+ size_t skip = 0;
+ std::vector<std::string> reasons = Split(reason, ",");
+ if (reasons.size() >= 2 && reasons[0] == "reboot" &&
+ (reasons[1] == "recovery" || reasons[1] == "bootloader" || reasons[1] == "cold" ||
+ reasons[1] == "hard" || reasons[1] == "warm")) {
+ skip = strlen("reboot,");
+ }
+ property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str() + skip);
+ sync();
+
+ bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;
+
+ auto shutdown_timeout = 0ms;
+ if (!SHUTDOWN_ZERO_TIMEOUT) {
+ if (is_thermal_shutdown) {
+ constexpr unsigned int thermal_shutdown_timeout = 1;
+ shutdown_timeout = std::chrono::seconds(thermal_shutdown_timeout);
+ } else {
+ constexpr unsigned int shutdown_timeout_default = 6;
+ auto shutdown_timeout_property = android::base::GetUintProperty(
+ "ro.build.shutdown_timeout", shutdown_timeout_default);
+ shutdown_timeout = std::chrono::seconds(shutdown_timeout_property);
+ }
+ }
+ LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms";
+
// keep debugging tools until non critical ones are all gone.
const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
// watchdogd is a vendor specific component but should be alive to complete shutdown safely.
@@ -356,7 +385,7 @@
}
// remaining operations (specifically fsck) may take a substantial duration
- if (cmd == ANDROID_RB_POWEROFF || cmd == ANDROID_RB_THERMOFF) {
+ if (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown) {
TurnOffBacklight();
}
@@ -429,81 +458,22 @@
if (kill_after_apps.count(s->name())) s->Stop();
}
// 4. sync, try umount, and optionally run fsck for user shutdown
- sync();
+ {
+ Timer sync_timer;
+ LOG(INFO) << "sync() before umount...";
+ sync();
+ LOG(INFO) << "sync() before umount took" << sync_timer;
+ }
UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
// Follow what linux shutdown is doing: one more sync with little bit delay
- sync();
- if (cmd != ANDROID_RB_THERMOFF) std::this_thread::sleep_for(100ms);
+ {
+ Timer sync_timer;
+ LOG(INFO) << "sync() after umount...";
+ sync();
+ LOG(INFO) << "sync() after umount took" << sync_timer;
+ }
+ if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);
LogShutdownTime(stat, &t);
-
- if (reboot_semaphore != nullptr) {
- sem_post(reboot_semaphore);
- }
-}
-
-void RunRebootThread(unsigned int cmd, std::chrono::milliseconds shutdown_timeout) {
- sem_t reboot_semaphore;
- timespec shutdown_timeout_timespec;
-
- if (sem_init(&reboot_semaphore, false, 0) == -1 ||
- clock_gettime(CLOCK_REALTIME, &shutdown_timeout_timespec) == -1) {
- // These should never fail, but if they do, skip the graceful reboot and reboot immediately.
- return;
- }
-
- std::thread reboot_thread(&RebootThread, cmd, shutdown_timeout, false, &reboot_semaphore);
- reboot_thread.detach();
-
- // One extra second than the timeout passed to the thread as there is a final Umount pass
- // after the timeout is reached.
- shutdown_timeout_timespec.tv_sec += 1 + shutdown_timeout.count() / 1000;
-
- int sem_return = 0;
- while ((sem_return = sem_timedwait(&reboot_semaphore, &shutdown_timeout_timespec)) == -1 &&
- errno == EINTR) {
- }
-
- if (sem_return == -1) {
- LOG(ERROR) << "Reboot thread timed out";
- }
-}
-
-void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
- bool runFsck) {
- LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
-
- // Ensure last reboot reason is reduced to canonical
- // alias reported in bootloader or system boot reason.
- size_t skip = 0;
- std::vector<std::string> reasons = Split(reason, ",");
- if (reasons.size() >= 2 && reasons[0] == "reboot" &&
- (reasons[1] == "recovery" || reasons[1] == "bootloader" || reasons[1] == "cold" ||
- reasons[1] == "hard" || reasons[1] == "warm")) {
- skip = strlen("reboot,");
- }
- property_set(LAST_REBOOT_REASON_PROPERTY, reason.c_str() + skip);
- sync();
-
- auto shutdown_timeout = 0ms;
- if (!SHUTDOWN_ZERO_TIMEOUT) {
- if (cmd == ANDROID_RB_THERMOFF) {
- constexpr auto kThermalShutdownTimeout = 1s;
- shutdown_timeout = kThermalShutdownTimeout;
- } else {
- constexpr unsigned int kShutdownTimeoutDefault = 6;
- auto shutdown_timeout_property = android::base::GetUintProperty(
- "ro.build.shutdown_timeout", kShutdownTimeoutDefault);
- shutdown_timeout = std::chrono::seconds(shutdown_timeout_property);
- }
- }
- LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " ms";
-
- if (runFsck) {
- RebootThread(cmd, shutdown_timeout, true, nullptr);
- } else {
- RunRebootThread(cmd, shutdown_timeout);
- }
-
// Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
RebootSystem(cmd, rebootTarget);
abort();
diff --git a/init/stable_properties.h b/init/stable_properties.h
index be35457..bd568f0 100644
--- a/init/stable_properties.h
+++ b/init/stable_properties.h
@@ -29,6 +29,7 @@
};
static const std::set<std::string> kExportedActionableProperties = {
+ "init.svc.console",
"init.svc.mediadrm",
"init.svc.zygote",
"persist.bluetooth.btsnoopenable",
diff --git a/init/uevent_listener.cpp b/init/uevent_listener.cpp
index ac1d7c7..24b14c4 100644
--- a/init/uevent_listener.cpp
+++ b/init/uevent_listener.cpp
@@ -83,8 +83,8 @@
}
UeventListener::UeventListener() {
- // is 256K enough? udev uses 16MB!
- device_fd_.reset(uevent_open_socket(256 * 1024, true));
+ // is 2MB enough? udev uses 128MB!
+ device_fd_.reset(uevent_open_socket(2 * 1024 * 1024, true));
if (device_fd_ == -1) {
LOG(FATAL) << "Could not open uevent socket";
}
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 711a12a..e087b2e 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -163,6 +163,9 @@
}
std::vector<std::string> skip_names{"libunwindstack.so", "libbacktrace.so"};
+ if (!skip_frames_) {
+ skip_names.clear();
+ }
return Backtrace::Unwind(regs.get(), GetMap(), &frames_, num_ignore_frames, &skip_names, &error_);
}
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index aab6db9..1e3d379 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -69,6 +69,9 @@
// Number of simultaneous threads running in our forked process.
#define NUM_PTRACE_THREADS 5
+// The list of shared libaries that make up the backtrace library.
+static std::vector<std::string> kBacktraceLibs{"libunwindstack.so", "libbacktrace.so"};
+
struct thread_t {
pid_t tid;
int32_t state;
@@ -256,16 +259,49 @@
VERIFY_NO_ERROR(backtrace->GetError().error_code);
ASSERT_TRUE(backtrace->NumFrames() != 0);
+ // None of the frames should be in the backtrace libraries.
for (const auto& frame : *backtrace ) {
if (BacktraceMap::IsValid(frame.map)) {
const std::string name = basename(frame.map.name.c_str());
- ASSERT_TRUE(name != "libunwind.so" && name != "libbacktrace.so")
- << DumpFrames(backtrace.get());
+ for (const auto& lib : kBacktraceLibs) {
+ ASSERT_TRUE(name != lib) << DumpFrames(backtrace.get());
+ }
}
- break;
}
}
+TEST(libbacktrace, local_unwind_frames) {
+ // Verify that a local unwind with the skip frames disabled does include
+ // frames within the backtrace libraries.
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ backtrace->SetSkipFrames(false);
+ ASSERT_TRUE(backtrace->Unwind(0));
+ VERIFY_NO_ERROR(backtrace->GetError().error_code);
+
+ ASSERT_TRUE(backtrace->NumFrames() != 0);
+ size_t first_frame_non_backtrace_lib = 0;
+ for (const auto& frame : *backtrace) {
+ if (BacktraceMap::IsValid(frame.map)) {
+ const std::string name = basename(frame.map.name.c_str());
+ bool found = false;
+ for (const auto& lib : kBacktraceLibs) {
+ if (name == lib) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ first_frame_non_backtrace_lib = frame.num;
+ break;
+ }
+ }
+ }
+
+ ASSERT_NE(0U, first_frame_non_backtrace_lib) << "No frames found in backtrace libraries:\n"
+ << DumpFrames(backtrace.get());
+}
+
TEST(libbacktrace, local_trace) {
ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelBacktrace, nullptr), 0);
}
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index a088207..735a2f3 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -204,6 +204,9 @@
std::string GetErrorString(BacktraceUnwindError error);
+ // Set whether to skip frames in libbacktrace/libunwindstack when doing a local unwind.
+ void SetSkipFrames(bool skip_frames) { skip_frames_ = skip_frames; }
+
protected:
Backtrace(pid_t pid, pid_t tid, BacktraceMap* map);
@@ -223,6 +226,9 @@
std::vector<backtrace_frame_data_t> frames_;
+ // Skip frames in libbacktrace/libunwindstack when doing a local unwind.
+ bool skip_frames_ = true;
+
BacktraceUnwindError error_;
};
diff --git a/libbacktrace/include/backtrace/BacktraceMap.h b/libbacktrace/include/backtrace/BacktraceMap.h
index 473d195..c94cad1 100644
--- a/libbacktrace/include/backtrace/BacktraceMap.h
+++ b/libbacktrace/include/backtrace/BacktraceMap.h
@@ -40,6 +40,10 @@
// Special flag to indicate a map is in /dev/. However, a map in
// /dev/ashmem/... does not set this flag.
static constexpr int PROT_DEVICE_MAP = 0x8000;
+// Special flag to indicate that this map represents an elf file
+// created by ART for use with the gdb jit debug interface.
+// This should only ever appear in offline maps data.
+static constexpr int PROT_JIT_SYMFILE_MAP = 0x4000;
struct backtrace_map_t {
uint64_t start = 0;
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index caca377..f872d0f 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -88,7 +88,6 @@
cc_test {
name: "memunreachable_binder_test",
defaults: ["libmemunreachable_defaults"],
- test_suites: ["vts"],
srcs: [
"tests/Binder_test.cpp",
],
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 124c70e..0d43f87 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -47,6 +47,8 @@
srcs: [
"ArmExidx.cpp",
+ "DexFile.cpp",
+ "DexFiles.cpp",
"DwarfCfa.cpp",
"DwarfEhFrameWithHdr.cpp",
"DwarfMemory.cpp",
@@ -85,11 +87,13 @@
},
vendor: {
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
- exclude_static_libs: ["libunwindstack_dex"],
+ exclude_srcs: [
+ "DexFile.cpp",
+ "DexFiles.cpp",
+ ],
exclude_shared_libs: ["libdexfile"],
},
},
- whole_static_libs: ["libunwindstack_dex"],
arch: {
x86: {
@@ -114,79 +118,9 @@
],
}
-// Isolate the dex file processing into a separate library. Currently,
-// it is necessary to add art include directories directly, which also
-// adds the art elf.h file in the include path, overriding the system one.
-// Work to isolate libdexfile is b/72216369.
-cc_library_static {
- name: "libunwindstack_dex",
- vendor_available: false,
- defaults: ["libunwindstack_flags"],
-
- cflags: [
- "-Wexit-time-destructors",
- ],
-
- srcs: [
- "DexFile.cpp",
- "DexFiles.cpp",
- ],
- target: {
- // Always disable optimizations for host to make it easier to debug.
- host: {
- cflags: [
- "-O0",
- "-g",
- ],
- },
- },
-
- shared_libs: [
- "libbase",
- "libdexfile",
- ],
- local_include_dirs: ["include"],
- allow_undefined_symbols: true,
-
- // libdexfile will eventually properly export headers, for now include
- // these directly.
- include_dirs: [
- "art/runtime",
- ],
-}
-
//-------------------------------------------------------------------------
// Unit Tests
//-------------------------------------------------------------------------
-cc_test_library {
- name: "libunwindstack_dex_test",
- vendor_available: false,
- defaults: ["libunwindstack_flags"],
-
- shared: {
- enabled: false,
- },
-
- srcs: [
- "tests/DexFileTest.cpp",
- "tests/DexFilesTest.cpp",
- ],
- local_include_dirs: ["include"],
- allow_undefined_symbols: true,
-
- shared_libs: [
- "libbase",
- "libunwindstack",
- "libdexfile",
- ],
-
- // libdexfile will eventually properly export headers, for now include
- // these directly.
- include_dirs: [
- "art/runtime",
- ],
-}
-
cc_test {
name: "libunwindstack_test",
defaults: ["libunwindstack_flags"],
@@ -194,6 +128,8 @@
srcs: [
"tests/ArmExidxDecodeTest.cpp",
"tests/ArmExidxExtractTest.cpp",
+ "tests/DexFileTest.cpp",
+ "tests/DexFilesTest.cpp",
"tests/DwarfCfaLogTest.cpp",
"tests/DwarfCfaTest.cpp",
"tests/DwarfDebugFrameTest.cpp",
@@ -242,22 +178,23 @@
"liblog",
"liblzma",
"libunwindstack",
+ "libdexfile",
],
static_libs: [
"libgmock",
],
- whole_static_libs: ["libunwindstack_dex_test"],
-
data: [
"tests/files/elf32.xz",
"tests/files/elf64.xz",
+ "tests/files/offline/art_quick_osr_stub_arm/*",
"tests/files/offline/bad_eh_frame_hdr_arm64/*",
"tests/files/offline/debug_frame_first_x86/*",
"tests/files/offline/eh_frame_hdr_begin_x86_64/*",
"tests/files/offline/jit_debug_arm/*",
"tests/files/offline/jit_debug_x86/*",
+ "tests/files/offline/jit_map_arm/*",
"tests/files/offline/gnu_debugdata_arm/*",
"tests/files/offline/straddle_arm/*",
"tests/files/offline/straddle_arm64/*",
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
index 4fc95c7..aa8cd3a 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -424,7 +424,10 @@
template <typename AddressType>
bool DwarfCfa<AddressType>::cfa_def_cfa_expression(dwarf_loc_regs_t* loc_regs) {
- (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_EXPRESSION,
+ // There is only one type of expression for CFA evaluation and the DWARF
+ // specification is unclear whether it returns the address or the
+ // dereferenced value. GDB expects the value, so will we.
+ (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_VAL_EXPRESSION,
.values = {operands_[0], memory_->cur_offset()}};
return true;
}
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 7649798..5586e72 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -190,8 +190,6 @@
// Always set the dex pc to zero when evaluating.
cur_regs->set_dex_pc(0);
- AddressType prev_cfa = regs->sp();
-
EvalInfo<AddressType> eval_info{.loc_regs = &loc_regs,
.cie = cie,
.regular_memory = regular_memory,
@@ -204,31 +202,16 @@
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
return false;
}
- // If the stack pointer register is the CFA, and the stack
- // pointer register does not have any associated location
- // information, use the current cfa value.
- if (regs->sp_reg() == loc->values[0] && loc_regs.count(regs->sp_reg()) == 0) {
- eval_info.cfa = prev_cfa;
- } else {
- eval_info.cfa = (*cur_regs)[loc->values[0]];
- }
+ eval_info.cfa = (*cur_regs)[loc->values[0]];
eval_info.cfa += loc->values[1];
break;
- case DWARF_LOCATION_EXPRESSION:
case DWARF_LOCATION_VAL_EXPRESSION: {
AddressType value;
if (!EvalExpression(*loc, regular_memory, &value, &eval_info.regs_info, nullptr)) {
return false;
}
- if (loc->type == DWARF_LOCATION_EXPRESSION) {
- if (!regular_memory->ReadFully(value, &eval_info.cfa, sizeof(AddressType))) {
- last_error_.code = DWARF_ERROR_MEMORY_INVALID;
- last_error_.address = value;
- return false;
- }
- } else {
- eval_info.cfa = value;
- }
+ // There is only one type of valid expression for CFA evaluation.
+ eval_info.cfa = value;
break;
}
default:
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index dfb8e8f..f93baeb 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -127,13 +127,9 @@
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()) {
- regs_arm->set_pc((*regs_arm)[ARM_REG_LR]);
- (*regs_arm)[ARM_REG_PC] = regs_arm->pc();
- } else {
- regs_arm->set_pc((*regs_arm)[ARM_REG_PC]);
+ (*regs_arm)[ARM_REG_PC] = (*regs_arm)[ARM_REG_LR];
}
- regs_arm->set_sp(arm.cfa());
- (*regs_arm)[ARM_REG_SP] = regs_arm->sp();
+ (*regs_arm)[ARM_REG_SP] = arm.cfa();
return_value = true;
// If the pc was set to zero, consider this the final frame.
@@ -171,4 +167,17 @@
return return_value;
}
+bool ElfInterfaceArm::GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
+ uint64_t* offset) {
+ // For ARM, thumb function symbols have bit 0 set, but the address passed
+ // in here might not have this bit set and result in a failure to find
+ // the thumb function names. Adjust the address and offset to account
+ // for this possible case.
+ if (ElfInterface32::GetFunctionName(addr | 1, load_bias, name, offset)) {
+ *offset &= ~1;
+ return true;
+ }
+ return false;
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/ElfInterfaceArm.h b/libunwindstack/ElfInterfaceArm.h
index 9c067ba..c1597ce 100644
--- a/libunwindstack/ElfInterfaceArm.h
+++ b/libunwindstack/ElfInterfaceArm.h
@@ -76,6 +76,9 @@
bool StepExidx(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
bool* finished);
+ bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
+ uint64_t* offset) override;
+
uint64_t start_offset() { return start_offset_; }
size_t total_entries() { return total_entries_; }
diff --git a/libunwindstack/Maps.cpp b/libunwindstack/Maps.cpp
index 4c16212..e1a1a71 100644
--- a/libunwindstack/Maps.cpp
+++ b/libunwindstack/Maps.cpp
@@ -25,6 +25,7 @@
#include <android-base/unique_fd.h>
+#include <algorithm>
#include <cctype>
#include <memory>
#include <string>
@@ -209,6 +210,11 @@
maps_.push_back(map_info);
}
+void Maps::Sort() {
+ std::sort(maps_.begin(), maps_.end(),
+ [](const MapInfo* a, const MapInfo* b) { return a->start < b->start; });
+}
+
Maps::~Maps() {
for (auto& map : maps_) {
delete map;
diff --git a/libunwindstack/RegsArm.cpp b/libunwindstack/RegsArm.cpp
index 5502ce1..e2a9cb0 100644
--- a/libunwindstack/RegsArm.cpp
+++ b/libunwindstack/RegsArm.cpp
@@ -28,13 +28,28 @@
namespace unwindstack {
-RegsArm::RegsArm()
- : RegsImpl<uint32_t>(ARM_REG_LAST, ARM_REG_SP, Location(LOCATION_REGISTER, ARM_REG_LR)) {}
+RegsArm::RegsArm() : RegsImpl<uint32_t>(ARM_REG_LAST, Location(LOCATION_REGISTER, ARM_REG_LR)) {}
ArchEnum RegsArm::Arch() {
return ARCH_ARM;
}
+uint64_t RegsArm::pc() {
+ return regs_[ARM_REG_PC];
+}
+
+uint64_t RegsArm::sp() {
+ return regs_[ARM_REG_SP];
+}
+
+void RegsArm::set_pc(uint64_t pc) {
+ regs_[ARM_REG_PC] = pc;
+}
+
+void RegsArm::set_sp(uint64_t sp) {
+ regs_[ARM_REG_SP] = sp;
+}
+
uint64_t RegsArm::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
uint64_t load_bias = elf->GetLoadBias();
if (rel_pc < load_bias) {
@@ -56,17 +71,13 @@
return 4;
}
-void RegsArm::SetFromRaw() {
- set_pc(regs_[ARM_REG_PC]);
- set_sp(regs_[ARM_REG_SP]);
-}
-
bool RegsArm::SetPcFromReturnAddress(Memory*) {
- if (pc() == regs_[ARM_REG_LR]) {
+ uint32_t lr = regs_[ARM_REG_LR];
+ if (regs_[ARM_REG_PC] == lr) {
return false;
}
- set_pc(regs_[ARM_REG_LR]);
+ regs_[ARM_REG_PC] = lr;
return true;
}
@@ -94,7 +105,6 @@
RegsArm* regs = new RegsArm();
memcpy(regs->RawData(), &user->regs[0], ARM_REG_LAST * sizeof(uint32_t));
- regs->SetFromRaw();
return regs;
}
@@ -103,7 +113,6 @@
RegsArm* regs = new RegsArm();
memcpy(regs->RawData(), &arm_ucontext->uc_mcontext.regs[0], ARM_REG_LAST * sizeof(uint32_t));
- regs->SetFromRaw();
return regs;
}
@@ -118,6 +127,7 @@
uint64_t offset = 0;
if (data == 0xe3a07077 || data == 0xef900077 || data == 0xdf002777) {
+ uint64_t sp = regs_[ARM_REG_SP];
// non-RT sigreturn call.
// __restore:
//
@@ -131,17 +141,18 @@
// Form 3 (thumb):
// 0x77 0x27 movs r7, #77
// 0x00 0xdf svc 0
- if (!process_memory->ReadFully(sp(), &data, sizeof(data))) {
+ if (!process_memory->ReadFully(sp, &data, sizeof(data))) {
return false;
}
if (data == 0x5ac3c35a) {
// SP + uc_mcontext offset + r0 offset.
- offset = sp() + 0x14 + 0xc;
+ offset = sp + 0x14 + 0xc;
} else {
// SP + r0 offset
- offset = sp() + 0xc;
+ offset = sp + 0xc;
}
} else if (data == 0xe3a070ad || data == 0xef9000ad || data == 0xdf0027ad) {
+ uint64_t sp = regs_[ARM_REG_SP];
// RT sigreturn call.
// __restore_rt:
//
@@ -155,15 +166,15 @@
// Form 3 (thumb):
// 0xad 0x27 movs r7, #ad
// 0x00 0xdf svc 0
- if (!process_memory->ReadFully(sp(), &data, sizeof(data))) {
+ if (!process_memory->ReadFully(sp, &data, sizeof(data))) {
return false;
}
- if (data == sp() + 8) {
+ if (data == sp + 8) {
// SP + 8 + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
- offset = sp() + 8 + 0x80 + 0x14 + 0xc;
+ offset = sp + 8 + 0x80 + 0x14 + 0xc;
} else {
// SP + sizeof(siginfo_t) + uc_mcontext_offset + r0 offset
- offset = sp() + 0x80 + 0x14 + 0xc;
+ offset = sp + 0x80 + 0x14 + 0xc;
}
}
if (offset == 0) {
@@ -173,7 +184,6 @@
if (!process_memory->ReadFully(offset, regs_.data(), sizeof(uint32_t) * ARM_REG_LAST)) {
return false;
}
- SetFromRaw();
return true;
}
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
index cc6f5ce..fe24c80 100644
--- a/libunwindstack/RegsArm64.cpp
+++ b/libunwindstack/RegsArm64.cpp
@@ -29,12 +29,28 @@
namespace unwindstack {
RegsArm64::RegsArm64()
- : RegsImpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
+ : RegsImpl<uint64_t>(ARM64_REG_LAST, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
ArchEnum RegsArm64::Arch() {
return ARCH_ARM64;
}
+uint64_t RegsArm64::pc() {
+ return regs_[ARM64_REG_PC];
+}
+
+uint64_t RegsArm64::sp() {
+ return regs_[ARM64_REG_SP];
+}
+
+void RegsArm64::set_pc(uint64_t pc) {
+ regs_[ARM64_REG_PC] = pc;
+}
+
+void RegsArm64::set_sp(uint64_t sp) {
+ regs_[ARM64_REG_SP] = sp;
+}
+
uint64_t RegsArm64::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
if (!elf->valid() || rel_pc < 4) {
return 0;
@@ -42,17 +58,13 @@
return 4;
}
-void RegsArm64::SetFromRaw() {
- set_pc(regs_[ARM64_REG_PC]);
- set_sp(regs_[ARM64_REG_SP]);
-}
-
bool RegsArm64::SetPcFromReturnAddress(Memory*) {
- if (pc() == regs_[ARM64_REG_LR]) {
+ uint64_t lr = regs_[ARM64_REG_LR];
+ if (regs_[ARM64_REG_PC] == lr) {
return false;
}
- set_pc(regs_[ARM64_REG_LR]);
+ regs_[ARM64_REG_PC] = lr;
return true;
}
@@ -100,7 +112,6 @@
uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
reg_data[ARM64_REG_PC] = user->pc;
reg_data[ARM64_REG_SP] = user->sp;
- regs->SetFromRaw();
return regs;
}
@@ -109,7 +120,6 @@
RegsArm64* regs = new RegsArm64();
memcpy(regs->RawData(), &arm64_ucontext->uc_mcontext.regs[0], ARM64_REG_LAST * sizeof(uint64_t));
- regs->SetFromRaw();
return regs;
}
@@ -131,12 +141,10 @@
}
// SP + sizeof(siginfo_t) + uc_mcontext offset + X0 offset.
- if (!process_memory->ReadFully(sp() + 0x80 + 0xb0 + 0x08, regs_.data(),
+ if (!process_memory->ReadFully(regs_[ARM64_REG_SP] + 0x80 + 0xb0 + 0x08, regs_.data(),
sizeof(uint64_t) * ARM64_REG_LAST)) {
return false;
}
-
- SetFromRaw();
return true;
}
diff --git a/libunwindstack/RegsMips.cpp b/libunwindstack/RegsMips.cpp
index 5d20bef..0b10e21 100644
--- a/libunwindstack/RegsMips.cpp
+++ b/libunwindstack/RegsMips.cpp
@@ -29,12 +29,28 @@
namespace unwindstack {
RegsMips::RegsMips()
- : RegsImpl<uint32_t>(MIPS_REG_LAST, MIPS_REG_SP, Location(LOCATION_REGISTER, MIPS_REG_RA)) {}
+ : RegsImpl<uint32_t>(MIPS_REG_LAST, Location(LOCATION_REGISTER, MIPS_REG_RA)) {}
ArchEnum RegsMips::Arch() {
return ARCH_MIPS;
}
+uint64_t RegsMips::pc() {
+ return regs_[MIPS_REG_PC];
+}
+
+uint64_t RegsMips::sp() {
+ return regs_[MIPS_REG_SP];
+}
+
+void RegsMips::set_pc(uint64_t pc) {
+ regs_[MIPS_REG_PC] = static_cast<uint32_t>(pc);
+}
+
+void RegsMips::set_sp(uint64_t sp) {
+ regs_[MIPS_REG_SP] = static_cast<uint32_t>(sp);
+}
+
uint64_t RegsMips::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
if (!elf->valid() || rel_pc < 8) {
return 0;
@@ -43,17 +59,13 @@
return 8;
}
-void RegsMips::SetFromRaw() {
- set_pc(regs_[MIPS_REG_PC]);
- set_sp(regs_[MIPS_REG_SP]);
-}
-
bool RegsMips::SetPcFromReturnAddress(Memory*) {
- if (pc() == regs_[MIPS_REG_RA]) {
+ uint32_t ra = regs_[MIPS_REG_RA];
+ if (regs_[MIPS_REG_PC] == ra) {
return false;
}
- set_pc(regs_[MIPS_REG_RA]);
+ regs_[MIPS_REG_PC] = ra;
return true;
}
@@ -101,7 +113,6 @@
memcpy(regs->RawData(), &user->regs[MIPS32_EF_R0], (MIPS_REG_R31 + 1) * sizeof(uint32_t));
reg_data[MIPS_REG_PC] = user->regs[MIPS32_EF_CP0_EPC];
- regs->SetFromRaw();
return regs;
}
@@ -114,7 +125,6 @@
(*regs)[MIPS_REG_R0 + i] = mips_ucontext->uc_mcontext.sc_regs[i];
}
(*regs)[MIPS_REG_PC] = mips_ucontext->uc_mcontext.sc_pc;
- regs->SetFromRaw();
return regs;
}
@@ -149,7 +159,7 @@
// read sc_pc and sc_regs[32] from stack
uint64_t values[MIPS_REG_LAST];
- if (!process_memory->Read(sp() + offset, values, sizeof(values))) {
+ if (!process_memory->Read(regs_[MIPS_REG_SP] + offset, values, sizeof(values))) {
return false;
}
@@ -160,8 +170,6 @@
for (int i = 0; i < 32; i++) {
regs_[MIPS_REG_R0 + i] = values[1 + i];
}
-
- SetFromRaw();
return true;
}
diff --git a/libunwindstack/RegsMips64.cpp b/libunwindstack/RegsMips64.cpp
index 4a03538..8848e3b 100644
--- a/libunwindstack/RegsMips64.cpp
+++ b/libunwindstack/RegsMips64.cpp
@@ -29,13 +29,28 @@
namespace unwindstack {
RegsMips64::RegsMips64()
- : RegsImpl<uint64_t>(MIPS64_REG_LAST, MIPS64_REG_SP,
- Location(LOCATION_REGISTER, MIPS64_REG_RA)) {}
+ : RegsImpl<uint64_t>(MIPS64_REG_LAST, Location(LOCATION_REGISTER, MIPS64_REG_RA)) {}
ArchEnum RegsMips64::Arch() {
return ARCH_MIPS64;
}
+uint64_t RegsMips64::pc() {
+ return regs_[MIPS64_REG_PC];
+}
+
+uint64_t RegsMips64::sp() {
+ return regs_[MIPS64_REG_SP];
+}
+
+void RegsMips64::set_pc(uint64_t pc) {
+ regs_[MIPS64_REG_PC] = pc;
+}
+
+void RegsMips64::set_sp(uint64_t sp) {
+ regs_[MIPS64_REG_SP] = sp;
+}
+
uint64_t RegsMips64::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
if (!elf->valid() || rel_pc < 8) {
return 0;
@@ -44,17 +59,13 @@
return 8;
}
-void RegsMips64::SetFromRaw() {
- set_pc(regs_[MIPS64_REG_PC]);
- set_sp(regs_[MIPS64_REG_SP]);
-}
-
bool RegsMips64::SetPcFromReturnAddress(Memory*) {
- if (pc() == regs_[MIPS64_REG_RA]) {
+ uint64_t ra = regs_[MIPS64_REG_RA];
+ if (regs_[MIPS64_REG_PC] == ra) {
return false;
}
- set_pc(regs_[MIPS64_REG_RA]);
+ regs_[MIPS64_REG_PC] = ra;
return true;
}
@@ -102,7 +113,6 @@
memcpy(regs->RawData(), &user->regs[MIPS64_EF_R0], (MIPS64_REG_R31 + 1) * sizeof(uint64_t));
reg_data[MIPS64_REG_PC] = user->regs[MIPS64_EF_CP0_EPC];
- regs->SetFromRaw();
return regs;
}
@@ -113,7 +123,6 @@
// Copy 64 bit sc_regs over to 64 bit regs
memcpy(regs->RawData(), &mips64_ucontext->uc_mcontext.sc_regs[0], 32 * sizeof(uint64_t));
(*regs)[MIPS64_REG_PC] = mips64_ucontext->uc_mcontext.sc_pc;
- regs->SetFromRaw();
return regs;
}
@@ -137,19 +146,17 @@
// vdso_rt_sigreturn => read rt_sigframe
// offset = siginfo offset + sizeof(siginfo) + uc_mcontext offset
// read 64 bit sc_regs[32] from stack into 64 bit regs_
- if (!process_memory->Read(sp() + 24 + 128 + 40, regs_.data(),
+ uint64_t sp = regs_[MIPS64_REG_SP];
+ if (!process_memory->Read(sp + 24 + 128 + 40, regs_.data(),
sizeof(uint64_t) * (MIPS64_REG_LAST - 1))) {
return false;
}
// offset = siginfo offset + sizeof(siginfo) + uc_mcontext offset + sc_pc offset
// read 64 bit sc_pc from stack into 64 bit regs_[MIPS64_REG_PC]
- if (!process_memory->Read(sp() + 24 + 128 + 40 + 576, ®s_[MIPS64_REG_PC],
- sizeof(uint64_t))) {
+ if (!process_memory->Read(sp + 24 + 128 + 40 + 576, ®s_[MIPS64_REG_PC], sizeof(uint64_t))) {
return false;
}
-
- SetFromRaw();
return true;
}
diff --git a/libunwindstack/RegsX86.cpp b/libunwindstack/RegsX86.cpp
index 573cb23..bb95a13 100644
--- a/libunwindstack/RegsX86.cpp
+++ b/libunwindstack/RegsX86.cpp
@@ -28,13 +28,28 @@
namespace unwindstack {
-RegsX86::RegsX86()
- : RegsImpl<uint32_t>(X86_REG_LAST, X86_REG_SP, Location(LOCATION_SP_OFFSET, -4)) {}
+RegsX86::RegsX86() : RegsImpl<uint32_t>(X86_REG_LAST, Location(LOCATION_SP_OFFSET, -4)) {}
ArchEnum RegsX86::Arch() {
return ARCH_X86;
}
+uint64_t RegsX86::pc() {
+ return regs_[X86_REG_PC];
+}
+
+uint64_t RegsX86::sp() {
+ return regs_[X86_REG_SP];
+}
+
+void RegsX86::set_pc(uint64_t pc) {
+ regs_[X86_REG_PC] = static_cast<uint32_t>(pc);
+}
+
+void RegsX86::set_sp(uint64_t sp) {
+ regs_[X86_REG_SP] = static_cast<uint32_t>(sp);
+}
+
uint64_t RegsX86::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
if (!elf->valid() || rel_pc == 0) {
return 0;
@@ -42,19 +57,15 @@
return 1;
}
-void RegsX86::SetFromRaw() {
- set_pc(regs_[X86_REG_PC]);
- 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->ReadFully(sp_, &new_pc, sizeof(new_pc)) || new_pc == pc()) {
+ if (!process_memory->ReadFully(regs_[X86_REG_SP], &new_pc, sizeof(new_pc)) ||
+ new_pc == regs_[X86_REG_PC]) {
return false;
}
- set_pc(new_pc);
+ regs_[X86_REG_PC] = new_pc;
return true;
}
@@ -84,7 +95,6 @@
(*regs)[X86_REG_ESP] = user->esp;
(*regs)[X86_REG_EIP] = user->eip;
- regs->SetFromRaw();
return regs;
}
@@ -99,7 +109,6 @@
regs_[X86_REG_ECX] = ucontext->uc_mcontext.ecx;
regs_[X86_REG_EAX] = ucontext->uc_mcontext.eax;
regs_[X86_REG_EIP] = ucontext->uc_mcontext.eip;
- SetFromRaw();
}
Regs* RegsX86::CreateFromUcontext(void* ucontext) {
@@ -131,7 +140,7 @@
// int signum
// struct sigcontext (same format as mcontext)
struct x86_mcontext_t context;
- if (!process_memory->ReadFully(sp() + 4, &context, sizeof(context))) {
+ if (!process_memory->ReadFully(regs_[X86_REG_SP] + 4, &context, sizeof(context))) {
return false;
}
regs_[X86_REG_EBP] = context.ebp;
@@ -141,7 +150,6 @@
regs_[X86_REG_ECX] = context.ecx;
regs_[X86_REG_EAX] = context.eax;
regs_[X86_REG_EIP] = context.eip;
- SetFromRaw();
return true;
} else if ((data & 0x00ffffffffffffffULL) == 0x0080cd000000adb8ULL) {
// With SA_SIGINFO set, the return sequence is:
@@ -157,7 +165,7 @@
// Get the location of the sigcontext data.
uint32_t ptr;
- if (!process_memory->ReadFully(sp() + 8, &ptr, sizeof(ptr))) {
+ if (!process_memory->ReadFully(regs_[X86_REG_SP] + 8, &ptr, sizeof(ptr))) {
return false;
}
// Only read the portion of the data structure we care about.
diff --git a/libunwindstack/RegsX86_64.cpp b/libunwindstack/RegsX86_64.cpp
index 3175a90..e57e2bc 100644
--- a/libunwindstack/RegsX86_64.cpp
+++ b/libunwindstack/RegsX86_64.cpp
@@ -28,13 +28,28 @@
namespace unwindstack {
-RegsX86_64::RegsX86_64()
- : RegsImpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP, Location(LOCATION_SP_OFFSET, -8)) {}
+RegsX86_64::RegsX86_64() : RegsImpl<uint64_t>(X86_64_REG_LAST, Location(LOCATION_SP_OFFSET, -8)) {}
ArchEnum RegsX86_64::Arch() {
return ARCH_X86_64;
}
+uint64_t RegsX86_64::pc() {
+ return regs_[X86_64_REG_PC];
+}
+
+uint64_t RegsX86_64::sp() {
+ return regs_[X86_64_REG_SP];
+}
+
+void RegsX86_64::set_pc(uint64_t pc) {
+ regs_[X86_64_REG_PC] = pc;
+}
+
+void RegsX86_64::set_sp(uint64_t sp) {
+ regs_[X86_64_REG_SP] = sp;
+}
+
uint64_t RegsX86_64::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
if (!elf->valid() || rel_pc == 0) {
return 0;
@@ -42,19 +57,15 @@
return 1;
}
-void RegsX86_64::SetFromRaw() {
- set_pc(regs_[X86_64_REG_PC]);
- 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->ReadFully(sp_, &new_pc, sizeof(new_pc)) || new_pc == pc()) {
+ if (!process_memory->ReadFully(regs_[X86_64_REG_SP], &new_pc, sizeof(new_pc)) ||
+ new_pc == regs_[X86_64_REG_PC]) {
return false;
}
- set_pc(new_pc);
+ regs_[X86_64_REG_PC] = new_pc;
return true;
}
@@ -100,7 +111,6 @@
(*regs)[X86_64_REG_RSP] = user->rsp;
(*regs)[X86_64_REG_RIP] = user->rip;
- regs->SetFromRaw();
return regs;
}
@@ -118,8 +128,6 @@
regs_[X86_64_REG_RCX] = ucontext->uc_mcontext.rcx;
regs_[X86_64_REG_RSP] = ucontext->uc_mcontext.rsp;
regs_[X86_64_REG_RIP] = ucontext->uc_mcontext.rip;
-
- SetFromRaw();
}
Regs* RegsX86_64::CreateFromUcontext(void* ucontext) {
@@ -152,7 +160,7 @@
// Read the mcontext data from the stack.
// sp points to the ucontext data structure, read only the mcontext part.
x86_64_ucontext_t x86_64_ucontext;
- if (!process_memory->ReadFully(sp() + 0x28, &x86_64_ucontext.uc_mcontext,
+ if (!process_memory->ReadFully(regs_[X86_64_REG_SP] + 0x28, &x86_64_ucontext.uc_mcontext,
sizeof(x86_64_mcontext_t))) {
return false;
}
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 94edb1c..9a6c6df 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -29,6 +29,7 @@
#include <unwindstack/Elf.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
#include <unwindstack/Unwinder.h>
#if !defined(NO_LIBDEXFILE_SUPPORT)
@@ -61,7 +62,9 @@
frame->map_offset = info->offset;
frame->map_load_bias = info->load_bias;
frame->map_flags = info->flags;
- frame->map_name = info->name;
+ if (resolve_names_) {
+ frame->map_name = info->name;
+ }
frame->rel_pc = dex_pc - info->start;
} else {
frame->rel_pc = dex_pc;
@@ -96,7 +99,9 @@
return;
}
- frame->map_name = map_info->name;
+ if (resolve_names_) {
+ frame->map_name = map_info->name;
+ }
frame->map_offset = map_info->offset;
frame->map_start = map_info->start;
frame->map_end = map_info->end;
@@ -138,26 +143,31 @@
uint64_t cur_sp = regs_->sp();
MapInfo* map_info = maps_->Find(regs_->pc());
- uint64_t rel_pc;
uint64_t pc_adjustment = 0;
uint64_t step_pc;
+ uint64_t rel_pc;
Elf* elf;
if (map_info == nullptr) {
- rel_pc = regs_->pc();
- step_pc = rel_pc;
+ step_pc = regs_->pc();
+ rel_pc = step_pc;
last_error_.code = ERROR_INVALID_MAP;
} else {
if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
break;
}
elf = map_info->GetElf(process_memory_, true);
- rel_pc = elf->GetRelPc(regs_->pc(), map_info);
+ step_pc = regs_->pc();
+ rel_pc = elf->GetRelPc(step_pc, map_info);
+ // Everyone except elf data in gdb jit debug maps uses the relative pc.
+ if (!(map_info->flags & MAPS_FLAGS_JIT_SYMFILE_MAP)) {
+ step_pc = rel_pc;
+ }
if (adjust_pc) {
pc_adjustment = regs_->GetPcAdjustment(rel_pc, elf);
} else {
pc_adjustment = 0;
}
- step_pc = rel_pc - pc_adjustment;
+ step_pc -= pc_adjustment;
// If the pc is in an invalid elf file, try and get an Elf object
// using the jit debug information.
diff --git a/libunwindstack/include/unwindstack/Maps.h b/libunwindstack/include/unwindstack/Maps.h
index 17a2d28..74e5c47 100644
--- a/libunwindstack/include/unwindstack/Maps.h
+++ b/libunwindstack/include/unwindstack/Maps.h
@@ -30,6 +30,10 @@
// Special flag to indicate a map is in /dev/. However, a map in
// /dev/ashmem/... does not set this flag.
static constexpr int MAPS_FLAGS_DEVICE_MAP = 0x8000;
+// Special flag to indicate that this map represents an elf file
+// created by ART for use with the gdb jit debug interface.
+// This should only ever appear in offline maps data.
+static constexpr int MAPS_FLAGS_JIT_SYMFILE_MAP = 0x4000;
class Maps {
public:
@@ -45,6 +49,8 @@
void Add(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, const std::string& name,
uint64_t load_bias);
+ void Sort();
+
typedef std::vector<MapInfo*>::iterator iterator;
iterator begin() { return maps_.begin(); }
iterator end() { return maps_.end(); }
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index b0e7ea1..4bac473 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -45,8 +45,8 @@
int16_t value;
};
- Regs(uint16_t total_regs, uint16_t sp_reg, const Location& return_loc)
- : total_regs_(total_regs), sp_reg_(sp_reg), return_loc_(return_loc) {}
+ Regs(uint16_t total_regs, const Location& return_loc)
+ : total_regs_(total_regs), return_loc_(return_loc) {}
virtual ~Regs() = default;
virtual ArchEnum Arch() = 0;
@@ -57,6 +57,9 @@
virtual uint64_t pc() = 0;
virtual uint64_t sp() = 0;
+ virtual void set_pc(uint64_t pc) = 0;
+ virtual void set_sp(uint64_t sp) = 0;
+
uint64_t dex_pc() { return dex_pc_; }
void set_dex_pc(uint64_t dex_pc) { dex_pc_ = dex_pc; }
@@ -64,13 +67,10 @@
virtual bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) = 0;
- virtual void SetFromRaw() = 0;
-
virtual bool SetPcFromReturnAddress(Memory* process_memory) = 0;
virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) = 0;
- uint16_t sp_reg() { return sp_reg_; }
uint16_t total_regs() { return total_regs_; }
static ArchEnum CurrentArch();
@@ -80,7 +80,6 @@
protected:
uint16_t total_regs_;
- uint16_t sp_reg_;
Location return_loc_;
uint64_t dex_pc_ = 0;
};
@@ -88,16 +87,10 @@
template <typename AddressType>
class RegsImpl : public Regs {
public:
- RegsImpl(uint16_t total_regs, uint16_t sp_reg, Location return_loc)
- : Regs(total_regs, sp_reg, return_loc), regs_(total_regs) {}
+ RegsImpl(uint16_t total_regs, Location return_loc)
+ : Regs(total_regs, return_loc), regs_(total_regs) {}
virtual ~RegsImpl() = default;
- uint64_t pc() override { return pc_; }
- uint64_t sp() override { return sp_; }
-
- void set_pc(AddressType pc) { pc_ = pc; }
- void set_sp(AddressType sp) { sp_ = sp; }
-
bool Is32Bit() override { return sizeof(AddressType) == sizeof(uint32_t); }
inline AddressType& operator[](size_t reg) { return regs_[reg]; }
@@ -111,8 +104,6 @@
}
protected:
- AddressType pc_;
- AddressType sp_;
std::vector<AddressType> regs_;
};
diff --git a/libunwindstack/include/unwindstack/RegsArm.h b/libunwindstack/include/unwindstack/RegsArm.h
index 5af90d3..31e6797 100644
--- a/libunwindstack/include/unwindstack/RegsArm.h
+++ b/libunwindstack/include/unwindstack/RegsArm.h
@@ -34,17 +34,21 @@
RegsArm();
virtual ~RegsArm() = default;
- virtual ArchEnum Arch() override final;
+ ArchEnum Arch() override final;
uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
- void SetFromRaw() override;
-
bool SetPcFromReturnAddress(Memory* process_memory) override;
bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
- virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+ void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+ uint64_t pc() override;
+ uint64_t sp() override;
+
+ void set_pc(uint64_t pc) override;
+ void set_sp(uint64_t sp) override;
static Regs* Read(void* data);
diff --git a/libunwindstack/include/unwindstack/RegsArm64.h b/libunwindstack/include/unwindstack/RegsArm64.h
index cb05732..0c45eba 100644
--- a/libunwindstack/include/unwindstack/RegsArm64.h
+++ b/libunwindstack/include/unwindstack/RegsArm64.h
@@ -34,17 +34,21 @@
RegsArm64();
virtual ~RegsArm64() = default;
- virtual ArchEnum Arch() override final;
+ ArchEnum Arch() override final;
uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
- void SetFromRaw() override;
-
bool SetPcFromReturnAddress(Memory* process_memory) override;
bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
- virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+ void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+ uint64_t pc() override;
+ uint64_t sp() override;
+
+ void set_pc(uint64_t pc) override;
+ void set_sp(uint64_t sp) override;
static Regs* Read(void* data);
diff --git a/libunwindstack/include/unwindstack/RegsGetLocal.h b/libunwindstack/include/unwindstack/RegsGetLocal.h
index 557eace..81c0af3 100644
--- a/libunwindstack/include/unwindstack/RegsGetLocal.h
+++ b/libunwindstack/include/unwindstack/RegsGetLocal.h
@@ -51,8 +51,6 @@
: [base] "+r"(reg_data)
:
: "memory");
-
- regs->SetFromRaw();
}
#elif defined(__aarch64__)
@@ -83,8 +81,6 @@
: [base] "+r"(reg_data)
:
: "x12", "x13", "memory");
-
- regs->SetFromRaw();
}
#elif defined(__i386__) || defined(__x86_64__) || defined(__mips__)
@@ -93,8 +89,6 @@
inline void RegsGetLocal(Regs* regs) {
AsmGetRegs(regs->RawData());
-
- regs->SetFromRaw();
}
#endif
diff --git a/libunwindstack/include/unwindstack/RegsMips.h b/libunwindstack/include/unwindstack/RegsMips.h
index 8e3c01f..709f9e2 100644
--- a/libunwindstack/include/unwindstack/RegsMips.h
+++ b/libunwindstack/include/unwindstack/RegsMips.h
@@ -34,17 +34,21 @@
RegsMips();
virtual ~RegsMips() = default;
- virtual ArchEnum Arch() override final;
+ ArchEnum Arch() override final;
uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
- void SetFromRaw() override;
-
bool SetPcFromReturnAddress(Memory* process_memory) override;
bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
- virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+ void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+ uint64_t pc() override;
+ uint64_t sp() override;
+
+ void set_pc(uint64_t pc) override;
+ void set_sp(uint64_t sp) override;
static Regs* Read(void* data);
diff --git a/libunwindstack/include/unwindstack/RegsMips64.h b/libunwindstack/include/unwindstack/RegsMips64.h
index 8c2d443..1de83ea 100644
--- a/libunwindstack/include/unwindstack/RegsMips64.h
+++ b/libunwindstack/include/unwindstack/RegsMips64.h
@@ -34,17 +34,21 @@
RegsMips64();
virtual ~RegsMips64() = default;
- virtual ArchEnum Arch() override final;
+ ArchEnum Arch() override final;
uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
- void SetFromRaw() override;
-
bool SetPcFromReturnAddress(Memory* process_memory) override;
bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
- virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+ void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+ uint64_t pc() override;
+ uint64_t sp() override;
+
+ void set_pc(uint64_t pc) override;
+ void set_sp(uint64_t sp) override;
static Regs* Read(void* data);
diff --git a/libunwindstack/include/unwindstack/RegsX86.h b/libunwindstack/include/unwindstack/RegsX86.h
index 1bc145d..586c9d8 100644
--- a/libunwindstack/include/unwindstack/RegsX86.h
+++ b/libunwindstack/include/unwindstack/RegsX86.h
@@ -35,19 +35,23 @@
RegsX86();
virtual ~RegsX86() = default;
- virtual ArchEnum Arch() override final;
+ ArchEnum Arch() override final;
uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
- 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);
- virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+ void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+ uint64_t pc() override;
+ uint64_t sp() override;
+
+ void set_pc(uint64_t pc) override;
+ void set_sp(uint64_t sp) override;
static Regs* Read(void* data);
diff --git a/libunwindstack/include/unwindstack/RegsX86_64.h b/libunwindstack/include/unwindstack/RegsX86_64.h
index 4cd45d4..061f479 100644
--- a/libunwindstack/include/unwindstack/RegsX86_64.h
+++ b/libunwindstack/include/unwindstack/RegsX86_64.h
@@ -35,19 +35,23 @@
RegsX86_64();
virtual ~RegsX86_64() = default;
- virtual ArchEnum Arch() override final;
+ ArchEnum Arch() override final;
uint64_t GetPcAdjustment(uint64_t rel_pc, Elf* elf) override;
- 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);
- virtual void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+ void IterateRegisters(std::function<void(const char*, uint64_t)>) override final;
+
+ uint64_t pc() override;
+ uint64_t sp() override;
+
+ void set_pc(uint64_t pc) override;
+ void set_sp(uint64_t sp) override;
static Regs* Read(void* data);
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
index 68dc30c..7395b04 100644
--- a/libunwindstack/tests/DwarfCfaTest.cpp
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -737,6 +737,8 @@
ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x284, &loc_regs));
ASSERT_EQ(0x284U, this->dmem_->cur_offset());
ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_VAL_EXPRESSION, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x81U, loc_regs[CFA_REG].values[0]);
ASSERT_EQ("", GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
index 6e15227..d424d5f 100644
--- a/libunwindstack/tests/DwarfOpTest.cpp
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -1468,7 +1468,7 @@
}
this->op_memory_.SetMemory(0, opcode_buffer);
- RegsImplFake<TypeParam> regs(32, 10);
+ RegsImplFake<TypeParam> regs(32);
for (size_t i = 0; i < 32; i++) {
regs[i] = i + 10;
}
@@ -1499,7 +1499,7 @@
};
this->op_memory_.SetMemory(0, opcode_buffer);
- RegsImplFake<TypeParam> regs(16, 10);
+ RegsImplFake<TypeParam> regs(16);
for (size_t i = 0; i < 16; i++) {
regs[i] = i + 10;
}
@@ -1526,7 +1526,7 @@
0x92, 0x80, 0x15, 0x80, 0x02};
this->op_memory_.SetMemory(0, opcode_buffer);
- RegsImplFake<TypeParam> regs(10, 10);
+ RegsImplFake<TypeParam> regs(10);
regs[5] = 0x45;
regs[6] = 0x190;
RegsInfo<TypeParam> regs_info(®s);
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index 37305b2..c85764c 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -92,14 +92,14 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_eval_fail) {
DwarfCie cie{.version = 3, .return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
regs.set_sp(0x2000);
regs[5] = 0x20;
regs[9] = 0x3000;
- loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5002}};
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x2, 0x5002}};
bool finished;
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->LastErrorCode());
@@ -108,7 +108,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_no_stack) {
DwarfCie cie{.version = 3, .return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -116,7 +116,7 @@
regs[5] = 0x20;
regs[9] = 0x3000;
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x96, 0x96, 0x96});
- loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5002}};
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x2, 0x5002}};
bool finished;
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->LastErrorCode());
@@ -124,7 +124,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr) {
DwarfCie cie{.version = 3, .return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -136,15 +136,13 @@
this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5004}};
bool finished;
- ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
- EXPECT_FALSE(finished);
- EXPECT_EQ(0x12345U, regs.sp());
- EXPECT_EQ(0x20U, regs.pc());
+ ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->LastErrorCode());
}
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_val_expr) {
DwarfCie cie{.version = 3, .return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -162,7 +160,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_is_register) {
DwarfCie cie{.version = 3, .return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -170,7 +168,7 @@
regs[5] = 0x20;
regs[9] = 0x3000;
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x50, 0x96, 0x96});
- loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5002}};
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x2, 0x5002}};
bool finished;
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
EXPECT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->section_->LastErrorCode());
@@ -178,7 +176,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_bad_regs) {
DwarfCie cie{.return_address_register = 60};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
bool finished;
@@ -188,7 +186,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_no_cfa) {
DwarfCie cie{.return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
bool finished;
@@ -198,7 +196,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_bad) {
DwarfCie cie{.return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {20, 0}};
@@ -227,7 +225,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_prev) {
DwarfCie cie{.return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -239,12 +237,12 @@
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());
+ EXPECT_EQ(0x3000U, regs.sp());
}
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_from_value) {
DwarfCie cie{.return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -262,7 +260,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_double_indirection) {
DwarfCie cie{.return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -283,7 +281,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_register_reference_chain) {
DwarfCie cie{.return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -314,7 +312,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_dex_pc) {
DwarfCie cie{.return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -333,7 +331,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_invalid_register) {
DwarfCie cie{.return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -348,7 +346,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_different_reg_locations) {
DwarfCie cie{.return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
if (sizeof(TypeParam) == sizeof(uint64_t)) {
@@ -382,7 +380,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address_undefined) {
DwarfCie cie{.return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -400,7 +398,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_pc_zero) {
DwarfCie cie{.return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -417,7 +415,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address) {
DwarfCie cie{.return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -434,7 +432,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_ignore_large_reg_loc) {
DwarfCie cie{.return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -453,7 +451,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_expr) {
DwarfCie cie{.version = 3, .return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
@@ -473,7 +471,7 @@
TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_val_expr) {
DwarfCie cie{.version = 3, .return_address_register = 5};
- RegsImplFake<TypeParam> regs(10, 9);
+ RegsImplFake<TypeParam> regs(10);
dwarf_loc_regs_t loc_regs;
regs.set_pc(0x100);
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
index ae9da5e..66207db 100644
--- a/libunwindstack/tests/ElfFake.cpp
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -65,8 +65,8 @@
}
RegsFake* fake_regs = reinterpret_cast<RegsFake*>(regs);
- fake_regs->FakeSetPc(entry.pc);
- fake_regs->FakeSetSp(entry.sp);
+ fake_regs->set_pc(entry.pc);
+ fake_regs->set_sp(entry.sp);
*finished = entry.finished;
return true;
}
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index eb85033..f9028c4 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -316,7 +316,6 @@
RegsArm regs;
regs[13] = 0x50000;
regs[15] = 0x8000;
- regs.SetFromRaw();
ElfInterfaceFake* interface = new ElfInterfaceFake(memory_);
elf.FakeSetInterface(interface);
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index ab23194..ede16b3 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -27,14 +27,16 @@
class RegsFake : public Regs {
public:
- RegsFake(uint16_t total_regs, uint16_t sp_reg)
- : Regs(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+ RegsFake(uint16_t total_regs) : Regs(total_regs, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
virtual ~RegsFake() = default;
ArchEnum Arch() override { return fake_arch_; }
void* RawData() override { return nullptr; }
uint64_t pc() override { return fake_pc_; }
uint64_t sp() override { return fake_sp_; }
+ void set_pc(uint64_t pc) override { fake_pc_ = pc; }
+ void set_sp(uint64_t sp) override { fake_sp_ = sp; }
+
bool SetPcFromReturnAddress(Memory*) override {
if (!fake_return_address_valid_) {
return false;
@@ -51,11 +53,7 @@
bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
- void SetFromRaw() override {}
-
void FakeSetArch(ArchEnum arch) { fake_arch_ = arch; }
- void FakeSetPc(uint64_t pc) { fake_pc_ = pc; }
- void FakeSetSp(uint64_t sp) { fake_sp_ = sp; }
void FakeSetDexPc(uint64_t dex_pc) { dex_pc_ = dex_pc; }
void FakeSetReturnAddress(uint64_t return_address) { fake_return_address_ = return_address; }
void FakeSetReturnAddressValid(bool valid) { fake_return_address_valid_ = valid; }
@@ -71,19 +69,23 @@
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)) {}
+ RegsImplFake(uint16_t total_regs)
+ : RegsImpl<TypeParam>(total_regs, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
virtual ~RegsImplFake() = default;
ArchEnum Arch() override { return ARCH_UNKNOWN; }
+ uint64_t pc() override { return fake_pc_; }
+ uint64_t sp() override { return fake_sp_; }
+ void set_pc(uint64_t pc) override { fake_pc_ = pc; }
+ void set_sp(uint64_t sp) override { fake_sp_ = sp; }
uint64_t GetPcAdjustment(uint64_t, Elf*) override { return 0; }
- void SetFromRaw() override {}
bool SetPcFromReturnAddress(Memory*) override { return false; }
bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
- void FakeSetPc(uint64_t pc) { this->pc_ = pc; }
- void FakeSetSp(uint64_t sp) { this->sp_ = sp; }
+ private:
+ uint64_t fake_pc_ = 0;
+ uint64_t fake_sp_ = 0;
};
} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
index ecd4051..eac12ca 100644
--- a/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
+++ b/libunwindstack/tests/RegsStepIfSignalHandlerTest.cpp
@@ -56,7 +56,6 @@
RegsArm regs;
regs[ARM_REG_PC] = 0x5000;
regs[ARM_REG_SP] = addr;
- regs.SetFromRaw();
elf_memory_->SetData32(0x5000, pc_data);
@@ -87,7 +86,6 @@
RegsArm regs;
regs[ARM_REG_PC] = 0x5000;
regs[ARM_REG_SP] = addr;
- regs.SetFromRaw();
elf_memory_->SetData32(0x5000, pc_data);
@@ -118,7 +116,6 @@
RegsArm64 regs;
regs[ARM64_REG_PC] = 0x8000;
regs[ARM64_REG_SP] = addr;
- regs.SetFromRaw();
elf_memory_->SetData64(0x8000, 0xd4000001d2801168ULL);
@@ -138,7 +135,6 @@
RegsX86 regs;
regs[X86_REG_EIP] = 0x4100;
regs[X86_REG_ESP] = addr;
- regs.SetFromRaw();
elf_memory_->SetData64(0x4100, 0x80cd00000077b858ULL);
for (uint64_t index = 0; index <= 25; index++) {
@@ -162,7 +158,6 @@
RegsX86 regs;
regs[X86_REG_EIP] = 0x4100;
regs[X86_REG_ESP] = addr;
- regs.SetFromRaw();
elf_memory_->SetData64(0x4100, 0x0080cd000000adb8ULL);
addr += 8;
@@ -191,7 +186,6 @@
RegsX86_64 regs;
regs[X86_64_REG_RIP] = 0x7000;
regs[X86_64_REG_RSP] = addr;
- regs.SetFromRaw();
elf_memory_->SetData64(0x7000, 0x0f0000000fc0c748);
elf_memory_->SetData16(0x7008, 0x0f05);
@@ -212,7 +206,6 @@
RegsMips regs;
regs[MIPS_REG_PC] = 0x8000;
regs[MIPS_REG_SP] = addr;
- regs.SetFromRaw();
elf_memory_->SetData64(0x8000, 0x0000000c24021017ULL);
@@ -232,7 +225,6 @@
RegsMips regs;
regs[MIPS_REG_PC] = 0x8000;
regs[MIPS_REG_SP] = addr;
- regs.SetFromRaw();
elf_memory_->SetData64(0x8000, 0x0000000c24021061ULL);
@@ -252,7 +244,6 @@
RegsMips64 regs;
regs[MIPS64_REG_PC] = 0x8000;
regs[MIPS64_REG_SP] = addr;
- regs.SetFromRaw();
elf_memory_->SetData64(0x8000, 0x0000000c2402145bULL);
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 8b2f6c8..3e80733 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -49,9 +49,8 @@
};
TEST_F(RegsTest, regs32) {
- RegsImplFake<uint32_t> regs32(50, 10);
+ RegsImplFake<uint32_t> regs32(50);
ASSERT_EQ(50U, regs32.total_regs());
- ASSERT_EQ(10U, regs32.sp_reg());
uint32_t* raw = reinterpret_cast<uint32_t*>(regs32.RawData());
for (size_t i = 0; i < 50; i++) {
@@ -72,9 +71,8 @@
}
TEST_F(RegsTest, regs64) {
- RegsImplFake<uint64_t> regs64(30, 12);
+ RegsImplFake<uint64_t> regs64(30);
ASSERT_EQ(30U, regs64.total_regs());
- ASSERT_EQ(12U, regs64.sp_reg());
uint64_t* raw = reinterpret_cast<uint64_t*>(regs64.RawData());
for (size_t i = 0; i < 30; i++) {
@@ -211,62 +209,56 @@
EXPECT_EQ(0U, regs_mips64.GetPcAdjustment(0xa00U, invalid_elf));
}
-TEST_F(RegsTest, arm_set_from_raw) {
+TEST_F(RegsTest, arm_verify_sp_pc) {
RegsArm arm;
uint32_t* regs = reinterpret_cast<uint32_t*>(arm.RawData());
regs[13] = 0x100;
regs[15] = 0x200;
- arm.SetFromRaw();
EXPECT_EQ(0x100U, arm.sp());
EXPECT_EQ(0x200U, arm.pc());
}
-TEST_F(RegsTest, arm64_set_from_raw) {
+TEST_F(RegsTest, arm64_verify_sp_pc) {
RegsArm64 arm64;
uint64_t* regs = reinterpret_cast<uint64_t*>(arm64.RawData());
regs[31] = 0xb100000000ULL;
regs[32] = 0xc200000000ULL;
- arm64.SetFromRaw();
EXPECT_EQ(0xb100000000U, arm64.sp());
EXPECT_EQ(0xc200000000U, arm64.pc());
}
-TEST_F(RegsTest, x86_set_from_raw) {
+TEST_F(RegsTest, x86_verify_sp_pc) {
RegsX86 x86;
uint32_t* regs = reinterpret_cast<uint32_t*>(x86.RawData());
regs[4] = 0x23450000;
regs[8] = 0xabcd0000;
- x86.SetFromRaw();
EXPECT_EQ(0x23450000U, x86.sp());
EXPECT_EQ(0xabcd0000U, x86.pc());
}
-TEST_F(RegsTest, x86_64_set_from_raw) {
+TEST_F(RegsTest, x86_64_verify_sp_pc) {
RegsX86_64 x86_64;
uint64_t* regs = reinterpret_cast<uint64_t*>(x86_64.RawData());
regs[7] = 0x1200000000ULL;
regs[16] = 0x4900000000ULL;
- x86_64.SetFromRaw();
EXPECT_EQ(0x1200000000U, x86_64.sp());
EXPECT_EQ(0x4900000000U, x86_64.pc());
}
-TEST_F(RegsTest, mips_set_from_raw) {
+TEST_F(RegsTest, mips_verify_sp_pc) {
RegsMips mips;
uint32_t* regs = reinterpret_cast<uint32_t*>(mips.RawData());
regs[29] = 0x100;
regs[32] = 0x200;
- mips.SetFromRaw();
EXPECT_EQ(0x100U, mips.sp());
EXPECT_EQ(0x200U, mips.pc());
}
-TEST_F(RegsTest, mips64_set_from_raw) {
+TEST_F(RegsTest, mips64_verify_sp_pc) {
RegsMips64 mips64;
uint64_t* regs = reinterpret_cast<uint64_t*>(mips64.RawData());
regs[29] = 0xb100000000ULL;
regs[32] = 0xc200000000ULL;
- mips64.SetFromRaw();
EXPECT_EQ(0xb100000000U, mips64.sp());
EXPECT_EQ(0xc200000000U, mips64.pc());
}
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index af4a5b5..6c242a5 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -18,6 +18,7 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
+#include <sys/mman.h>
#include <unistd.h>
#include <gtest/gtest.h>
@@ -122,7 +123,6 @@
(*regs)[entry->second] = value;
}
fclose(fp);
- regs->SetFromRaw();
}
static std::unordered_map<std::string, uint32_t> arm_regs_;
@@ -188,7 +188,7 @@
std::string frame_info(DumpFrames(unwinder));
ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
- " #00 pc 0001a9f8 libc.so (abort+63)\n"
+ " #00 pc 0001a9f8 libc.so (abort+64)\n"
" #01 pc 00006a1b libbase.so (_ZN7android4base14DefaultAborterEPKc+6)\n"
" #02 pc 00007441 libbase.so (_ZN7android4base10LogMessageD2Ev+748)\n"
" #03 pc 00015147 /does/not/exist/libhidlbase.so\n",
@@ -575,7 +575,7 @@
std::string frame_info(DumpFrames(unwinder));
ASSERT_EQ(76U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
EXPECT_EQ(
- " #00 pc 00018a5e libarttestd.so (Java_Main_unwindInProcess+865)\n"
+ " #00 pc 00018a5e libarttestd.so (Java_Main_unwindInProcess+866)\n"
" #01 pc 0000212d (offset 0x2000) 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
"boolean)+92)\n"
" #02 pc 00011cb1 anonymous:e2796000 (boolean Main.bar(boolean)+72)\n"
@@ -956,4 +956,159 @@
EXPECT_EQ(0x7ffcc85971a0U, unwinder.frames()[4].sp);
}
+TEST_F(UnwindOfflineTest, art_quick_osr_stub_arm) {
+ Init("art_quick_osr_stub_arm/", ARCH_ARM);
+
+ MemoryOfflineParts* memory = new MemoryOfflineParts;
+ AddMemory(dir_ + "descriptor.data", memory);
+ AddMemory(dir_ + "stack.data", memory);
+ for (size_t i = 0; i < 2; i++) {
+ AddMemory(dir_ + "entry" + std::to_string(i) + ".data", memory);
+ AddMemory(dir_ + "jit" + std::to_string(i) + ".data", memory);
+ }
+ process_memory_.reset(memory);
+
+ JitDebug jit_debug(process_memory_);
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.SetJitDebug(&jit_debug, regs_->Arch());
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(25U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0000c788 <anonymous:d0250000> "
+ "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity.access$000)\n"
+ " #01 pc 0000cdd5 <anonymous:d0250000> "
+ "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run+60)\n"
+ " #02 pc 004135bb libart.so (art_quick_osr_stub+42)\n"
+ " #03 pc 002657a5 libart.so "
+ "(_ZN3art3jit3Jit25MaybeDoOnStackReplacementEPNS_6ThreadEPNS_9ArtMethodEjiPNS_6JValueE+876)\n"
+ " #04 pc 004021a7 libart.so (MterpMaybeDoOnStackReplacement+86)\n"
+ " #05 pc 00412474 libart.so (ExecuteMterpImpl+66164)\n"
+ " #06 pc cd8365b0 <unknown>\n" // symbol in dex file
+ " #07 pc 001d7f1b libart.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+374)\n"
+ " #08 pc 001dc593 libart.so "
+ "(_ZN3art11interpreter33ArtInterpreterToInterpreterBridgeEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameEPNS_6JValueE+154)\n"
+ " #09 pc 001f4d01 libart.so "
+ "(_ZN3art11interpreter6DoCallILb0ELb0EEEbPNS_9ArtMethodEPNS_6ThreadERNS_11ShadowFrameEPKNS_"
+ "11InstructionEtPNS_6JValueE+732)\n"
+ " #10 pc 003fe427 libart.so (MterpInvokeInterface+1354)\n"
+ " #11 pc 00405b94 libart.so (ExecuteMterpImpl+14740)\n"
+ " #12 pc 7004873e <unknown>\n" // symbol in dex file
+ " #13 pc 001d7f1b libart.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+374)\n"
+ " #14 pc 001dc4d5 libart.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+92)\n"
+ " #15 pc 003f25ab libart.so (artQuickToInterpreterBridge+970)\n"
+ " #16 pc 00417aff libart.so (art_quick_to_interpreter_bridge+30)\n"
+ " #17 pc 00413575 libart.so (art_quick_invoke_stub_internal+68)\n"
+ " #18 pc 00418531 libart.so (art_quick_invoke_stub+236)\n"
+ " #19 pc 000b468d libart.so (_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+136)\n"
+ " #20 pc 00362f49 libart.so "
+ "(_ZN3art12_GLOBAL__N_118InvokeWithArgArrayERKNS_33ScopedObjectAccessAlreadyRunnableEPNS_"
+ "9ArtMethodEPNS0_8ArgArrayEPNS_6JValueEPKc+52)\n"
+ " #21 pc 00363cd9 libart.so "
+ "(_ZN3art35InvokeVirtualOrInterfaceWithJValuesERKNS_33ScopedObjectAccessAlreadyRunnableEP8_"
+ "jobjectP10_jmethodIDP6jvalue+332)\n"
+ " #22 pc 003851dd libart.so (_ZN3art6Thread14CreateCallbackEPv+868)\n"
+ " #23 pc 00062925 libc.so (_ZL15__pthread_startPv+22)\n"
+ " #24 pc 0001de39 libc.so (__start_thread+24)\n",
+ frame_info);
+ EXPECT_EQ(0xd025c788U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xcd4ff140U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0xd025cdd5U, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xcd4ff140U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0xe4a755bbU, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xcd4ff160U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0xe48c77a5U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xcd4ff190U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0xe4a641a7U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xcd4ff298U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0xe4a74474U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0xcd4ff2b8U, unwinder.frames()[5].sp);
+ EXPECT_EQ(0xcd8365b0U, unwinder.frames()[6].pc);
+ EXPECT_EQ(0xcd4ff2e0U, unwinder.frames()[6].sp);
+ EXPECT_EQ(0xe4839f1bU, unwinder.frames()[7].pc);
+ EXPECT_EQ(0xcd4ff2e0U, unwinder.frames()[7].sp);
+ EXPECT_EQ(0xe483e593U, unwinder.frames()[8].pc);
+ EXPECT_EQ(0xcd4ff330U, unwinder.frames()[8].sp);
+ EXPECT_EQ(0xe4856d01U, unwinder.frames()[9].pc);
+ EXPECT_EQ(0xcd4ff380U, unwinder.frames()[9].sp);
+ EXPECT_EQ(0xe4a60427U, unwinder.frames()[10].pc);
+ EXPECT_EQ(0xcd4ff430U, unwinder.frames()[10].sp);
+ EXPECT_EQ(0xe4a67b94U, unwinder.frames()[11].pc);
+ EXPECT_EQ(0xcd4ff498U, unwinder.frames()[11].sp);
+ EXPECT_EQ(0x7004873eU, unwinder.frames()[12].pc);
+ EXPECT_EQ(0xcd4ff4c0U, unwinder.frames()[12].sp);
+ EXPECT_EQ(0xe4839f1bU, unwinder.frames()[13].pc);
+ EXPECT_EQ(0xcd4ff4c0U, unwinder.frames()[13].sp);
+ EXPECT_EQ(0xe483e4d5U, unwinder.frames()[14].pc);
+ EXPECT_EQ(0xcd4ff510U, unwinder.frames()[14].sp);
+ EXPECT_EQ(0xe4a545abU, unwinder.frames()[15].pc);
+ EXPECT_EQ(0xcd4ff538U, unwinder.frames()[15].sp);
+ EXPECT_EQ(0xe4a79affU, unwinder.frames()[16].pc);
+ EXPECT_EQ(0xcd4ff640U, unwinder.frames()[16].sp);
+ EXPECT_EQ(0xe4a75575U, unwinder.frames()[17].pc);
+ EXPECT_EQ(0xcd4ff6b0U, unwinder.frames()[17].sp);
+ EXPECT_EQ(0xe4a7a531U, unwinder.frames()[18].pc);
+ EXPECT_EQ(0xcd4ff6e8U, unwinder.frames()[18].sp);
+ EXPECT_EQ(0xe471668dU, unwinder.frames()[19].pc);
+ EXPECT_EQ(0xcd4ff770U, unwinder.frames()[19].sp);
+ EXPECT_EQ(0xe49c4f49U, unwinder.frames()[20].pc);
+ EXPECT_EQ(0xcd4ff7c8U, unwinder.frames()[20].sp);
+ EXPECT_EQ(0xe49c5cd9U, unwinder.frames()[21].pc);
+ EXPECT_EQ(0xcd4ff850U, unwinder.frames()[21].sp);
+ EXPECT_EQ(0xe49e71ddU, unwinder.frames()[22].pc);
+ EXPECT_EQ(0xcd4ff8e8U, unwinder.frames()[22].sp);
+ EXPECT_EQ(0xe7df3925U, unwinder.frames()[23].pc);
+ EXPECT_EQ(0xcd4ff958U, unwinder.frames()[23].sp);
+ EXPECT_EQ(0xe7daee39U, unwinder.frames()[24].pc);
+ EXPECT_EQ(0xcd4ff960U, unwinder.frames()[24].sp);
+}
+
+TEST_F(UnwindOfflineTest, jit_map_arm) {
+ Init("jit_map_arm/", ARCH_ARM);
+
+ maps_->Add(0xd025c788, 0xd025c9f0, 0, PROT_READ | PROT_EXEC | MAPS_FLAGS_JIT_SYMFILE_MAP,
+ "jit_map0.so", 0);
+ maps_->Add(0xd025cd98, 0xd025cff4, 0, PROT_READ | PROT_EXEC | MAPS_FLAGS_JIT_SYMFILE_MAP,
+ "jit_map1.so", 0);
+ maps_->Sort();
+
+ Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
+ unwinder.Unwind();
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(6U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 00000000 jit_map0.so "
+ "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity.access$000)\n"
+ " #01 pc 0000003d jit_map1.so "
+ "(com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run+60)\n"
+ " #02 pc 004135bb libart.so (art_quick_osr_stub+42)\n"
+
+ " #03 pc 003851dd libart.so (_ZN3art6Thread14CreateCallbackEPv+868)\n"
+ " #04 pc 00062925 libc.so (_ZL15__pthread_startPv+22)\n"
+ " #05 pc 0001de39 libc.so (__start_thread+24)\n",
+ frame_info);
+
+ EXPECT_EQ(0xd025c788U, unwinder.frames()[0].pc);
+ EXPECT_EQ(0xcd4ff140U, unwinder.frames()[0].sp);
+ EXPECT_EQ(0xd025cdd5U, unwinder.frames()[1].pc);
+ EXPECT_EQ(0xcd4ff140U, unwinder.frames()[1].sp);
+ EXPECT_EQ(0xe4a755bbU, unwinder.frames()[2].pc);
+ EXPECT_EQ(0xcd4ff160U, unwinder.frames()[2].sp);
+ EXPECT_EQ(0xe49e71ddU, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xcd4ff8e8U, unwinder.frames()[3].sp);
+ EXPECT_EQ(0xe7df3925U, unwinder.frames()[4].pc);
+ EXPECT_EQ(0xcd4ff958U, unwinder.frames()[4].sp);
+ EXPECT_EQ(0xe7daee39U, unwinder.frames()[5].pc);
+ EXPECT_EQ(0xcd4ff960U, unwinder.frames()[5].sp);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp
index e44b225..2428f68 100644
--- a/libunwindstack/tests/UnwinderTest.cpp
+++ b/libunwindstack/tests/UnwinderTest.cpp
@@ -133,7 +133,7 @@
};
MapsFake UnwinderTest::maps_;
-RegsFake UnwinderTest::regs_(5, 0);
+RegsFake UnwinderTest::regs_(5);
std::shared_ptr<Memory> UnwinderTest::process_memory_(nullptr);
TEST_F(UnwinderTest, multiple_frames) {
@@ -141,8 +141,8 @@
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
- regs_.FakeSetPc(0x1000);
- regs_.FakeSetSp(0x10000);
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
@@ -201,8 +201,8 @@
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
- regs_.FakeSetPc(0x1000);
- regs_.FakeSetSp(0x10000);
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
@@ -221,7 +221,7 @@
EXPECT_EQ(0x10000U, frame->sp);
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ("", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
@@ -235,7 +235,7 @@
EXPECT_EQ(0x10010U, frame->sp);
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ("", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
@@ -249,7 +249,7 @@
EXPECT_EQ(0x10020U, frame->sp);
EXPECT_EQ("", frame->function_name);
EXPECT_EQ(0U, frame->function_offset);
- EXPECT_EQ("/system/fake/libc.so", frame->map_name);
+ EXPECT_EQ("", frame->map_name);
EXPECT_EQ(0U, frame->map_offset);
EXPECT_EQ(0x1000U, frame->map_start);
EXPECT_EQ(0x8000U, frame->map_end);
@@ -260,8 +260,8 @@
TEST_F(UnwinderTest, non_zero_load_bias) {
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
- regs_.FakeSetPc(0xa5500);
- regs_.FakeSetSp(0x10000);
+ regs_.set_pc(0xa5500);
+ regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, &maps_, ®s_, process_memory_);
@@ -288,8 +288,8 @@
TEST_F(UnwinderTest, non_zero_elf_offset) {
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
- regs_.FakeSetPc(0xa7500);
- regs_.FakeSetSp(0x10000);
+ regs_.set_pc(0xa7500);
+ regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, &maps_, ®s_, process_memory_);
@@ -316,8 +316,8 @@
TEST_F(UnwinderTest, non_zero_map_offset) {
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
- regs_.FakeSetPc(0x43000);
- regs_.FakeSetSp(0x10000);
+ regs_.set_pc(0x43000);
+ regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
Unwinder unwinder(64, &maps_, ®s_, process_memory_);
@@ -349,8 +349,8 @@
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame3", 3));
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame4", 4));
- regs_.FakeSetPc(0x1000);
- regs_.FakeSetSp(0x10000);
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0x1000, 0x10000, true));
ElfInterfaceFake::FakePushStepData(StepData(0x1102, 0x10010, false));
ElfInterfaceFake::FakePushStepData(StepData(0x1202, 0x10020, false));
@@ -383,8 +383,8 @@
ElfInterfaceFake::FakePushStepData(StepData(0x1102 + i * 0x100, 0x10010 + i * 0x10, false));
}
- regs_.FakeSetPc(0x1000);
- regs_.FakeSetSp(0x10000);
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
Unwinder unwinder(20, &maps_, ®s_, process_memory_);
unwinder.Unwind();
@@ -415,8 +415,8 @@
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
- regs_.FakeSetPc(0x20000);
- regs_.FakeSetSp(0x10000);
+ regs_.set_pc(0x20000);
+ regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false));
ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0x20002, 0x10030, false));
@@ -481,8 +481,8 @@
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
- regs_.FakeSetPc(0x1000);
- regs_.FakeSetSp(0x63000);
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x63000);
ElfInterfaceFake::FakePushStepData(StepData(0x21002, 0x50020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
@@ -527,8 +527,8 @@
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
- regs_.FakeSetPc(0x13000);
- regs_.FakeSetSp(0x10000);
+ regs_.set_pc(0x13000);
+ regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false));
ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
@@ -546,8 +546,8 @@
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame2", 2));
- regs_.FakeSetPc(0x1000);
- regs_.FakeSetSp(0x13000);
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x13000);
ElfInterfaceFake::FakePushStepData(StepData(0x23002, 0x10010, false));
ElfInterfaceFake::FakePushStepData(StepData(0x23102, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
@@ -563,8 +563,8 @@
TEST_F(UnwinderTest, pc_without_map) {
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
- regs_.FakeSetPc(0x41000);
- regs_.FakeSetSp(0x13000);
+ regs_.set_pc(0x41000);
+ regs_.set_sp(0x13000);
Unwinder unwinder(64, &maps_, ®s_, process_memory_);
unwinder.Unwind();
@@ -593,8 +593,8 @@
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
// Fake as if code called a nullptr function.
- regs_.FakeSetPc(0);
- regs_.FakeSetSp(0x10000);
+ regs_.set_pc(0);
+ regs_.set_sp(0x10000);
regs_.FakeSetReturnAddress(0x1202);
regs_.FakeSetReturnAddressValid(true);
@@ -657,8 +657,8 @@
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
// Fake as if code called a nullptr function.
- regs_.FakeSetPc(0);
- regs_.FakeSetSp(0x10000);
+ regs_.set_pc(0);
+ regs_.set_sp(0x10000);
regs_.FakeSetReturnAddress(0x1202);
regs_.FakeSetReturnAddressValid(true);
@@ -691,8 +691,8 @@
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame3", 3));
// Fake as if code called a nullptr function.
- regs_.FakeSetPc(0x1000);
- regs_.FakeSetSp(0x10000);
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0x43402, 0x10010, false));
ElfInterfaceFake::FakePushStepData(StepData(0x53502, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
@@ -745,8 +745,8 @@
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame3", 3));
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame4", 4));
- regs_.FakeSetPc(0x1000);
- regs_.FakeSetSp(0x10000);
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
ElfInterfaceFake::FakePushStepData(StepData(0x33402, 0x10010, false));
ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
ElfInterfaceFake::FakePushStepData(StepData(0x33502, 0x10020, false));
@@ -805,8 +805,8 @@
TEST_F(UnwinderTest, dex_pc_in_map) {
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
- regs_.FakeSetPc(0x1000);
- regs_.FakeSetSp(0x10000);
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
regs_.FakeSetDexPc(0xa3400);
Unwinder unwinder(64, &maps_, ®s_, process_memory_);
@@ -846,8 +846,8 @@
TEST_F(UnwinderTest, dex_pc_not_in_map) {
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
- regs_.FakeSetPc(0x1000);
- regs_.FakeSetSp(0x10000);
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
regs_.FakeSetDexPc(0x50000);
Unwinder unwinder(64, &maps_, ®s_, process_memory_);
@@ -888,8 +888,8 @@
TEST_F(UnwinderTest, dex_pc_multiple_frames) {
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame0", 0));
ElfInterfaceFake::FakePushFunctionData(FunctionData("Frame1", 1));
- regs_.FakeSetPc(0x1000);
- regs_.FakeSetSp(0x10000);
+ regs_.set_pc(0x1000);
+ regs_.set_sp(0x10000);
regs_.FakeSetDexPc(0xa3400);
ElfInterfaceFake::FakePushStepData(StepData(0x33402, 0x10010, false));
ElfInterfaceFake::FakePushStepData(StepData(0, 0, true));
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/descriptor.data b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/descriptor.data
new file mode 100644
index 0000000..300646b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/descriptor.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry0.data b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry0.data
new file mode 100644
index 0000000..999cb79
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry1.data b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry1.data
new file mode 100644
index 0000000..6aa1c82
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/entry1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit0.data b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit0.data
new file mode 100644
index 0000000..19d7b65
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit1.data b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit1.data
new file mode 100644
index 0000000..edcd3e1
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/jit1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libart.so b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libart.so
new file mode 100644
index 0000000..09ba495
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libart.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libc.so b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libc.so
new file mode 100644
index 0000000..39c9025
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt
new file mode 100644
index 0000000..55aaaf6
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/maps.txt
@@ -0,0 +1,3 @@
+d0250000-d2600000 r-xp 0 00:00 0 <anonymous:d0250000>
+e466e000-e4ae8000 r-xp 0 00:00 0 libart.so
+e7d91000-e7e31000 r-xp 0 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/regs.txt b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/regs.txt
new file mode 100644
index 0000000..0b51814
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: e814103c
+r1: 12dcf218
+r2: 1a90df75
+r3: ffffffbf
+r4: 0
+r5: 12dc0800
+r6: 12dcf218
+r7: 1a90df75
+r8: 0
+r9: dd23cc00
+r10: 1c
+r11: cd4ff16c
+ip: 0
+sp: cd4ff140
+lr: d025cdd7
+pc: d025c788
diff --git a/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/stack.data b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/stack.data
new file mode 100644
index 0000000..f00917b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/art_quick_osr_stub_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/jit_map0.so b/libunwindstack/tests/files/offline/jit_map_arm/jit_map0.so
new file mode 100644
index 0000000..e667883
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/jit_map0.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/jit_map1.so b/libunwindstack/tests/files/offline/jit_map_arm/jit_map1.so
new file mode 100644
index 0000000..9a1d714
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/jit_map1.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/libart.so b/libunwindstack/tests/files/offline/jit_map_arm/libart.so
new file mode 100644
index 0000000..09ba495
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/libart.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/libc.so b/libunwindstack/tests/files/offline/jit_map_arm/libc.so
new file mode 100644
index 0000000..39c9025
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/maps.txt b/libunwindstack/tests/files/offline/jit_map_arm/maps.txt
new file mode 100644
index 0000000..5aaec54
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/maps.txt
@@ -0,0 +1,2 @@
+e466e000-e4ae8000 r-xp 0 00:00 0 libart.so
+e7d91000-e7e31000 r-xp 0 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/regs.txt b/libunwindstack/tests/files/offline/jit_map_arm/regs.txt
new file mode 100644
index 0000000..0b51814
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/regs.txt
@@ -0,0 +1,16 @@
+r0: e814103c
+r1: 12dcf218
+r2: 1a90df75
+r3: ffffffbf
+r4: 0
+r5: 12dc0800
+r6: 12dcf218
+r7: 1a90df75
+r8: 0
+r9: dd23cc00
+r10: 1c
+r11: cd4ff16c
+ip: 0
+sp: cd4ff140
+lr: d025cdd7
+pc: d025c788
diff --git a/libunwindstack/tests/files/offline/jit_map_arm/stack.data b/libunwindstack/tests/files/offline/jit_map_arm/stack.data
new file mode 100644
index 0000000..fb8feeb
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_map_arm/stack.data
Binary files differ
diff --git a/libunwindstack/tools/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
index a0abcca..5a8edfd 100644
--- a/libunwindstack/tools/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -53,8 +53,7 @@
printf(" PC 0x%" PRIx64, addr + load_bias);
uint64_t func_offset;
uint64_t pc = addr + load_bias;
- // This might be a thumb function, so set the low bit.
- if (interface->GetFunctionName(pc | 1, load_bias, &name, &func_offset) && !name.empty()) {
+ if (interface->GetFunctionName(pc, load_bias, &name, &func_offset) && !name.empty()) {
printf(" <%s>", name.c_str());
}
printf("\n");
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 32caa69..0d7925a 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -159,7 +159,6 @@
cc_library {
name: "libutilscallstack",
defaults: ["libutils_defaults"],
- vendor_available: false,
srcs: [
"CallStack.cpp",
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
index 76d308a..d172755 100644
--- a/lmkd/Android.bp
+++ b/lmkd/Android.bp
@@ -6,6 +6,7 @@
"liblog",
"libcutils",
],
+ local_include_dirs: ["include"],
cflags: ["-Werror"],
init_rc: ["lmkd.rc"],
@@ -18,3 +19,17 @@
},
},
}
+
+cc_library_static {
+ name: "liblmkd_utils",
+ srcs: ["liblmkd_utils.c"],
+ shared_libs: [
+ "libcutils",
+ ],
+ export_include_dirs: ["include"],
+ cppflags: [
+ "-g",
+ "-Wall",
+ "-Werror",
+ ]
+}
diff --git a/lmkd/include/liblmkd_utils.h b/lmkd/include/liblmkd_utils.h
new file mode 100644
index 0000000..72e3f4a
--- /dev/null
+++ b/lmkd/include/liblmkd_utils.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2018 Google, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBLMKD_UTILS_H_
+#define _LIBLMKD_UTILS_H_
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <lmkd.h>
+
+__BEGIN_DECLS
+
+/*
+ * Connects to lmkd process and returns socket handle.
+ * On success returns socket handle.
+ * On error, -1 is returned, and errno is set appropriately.
+ */
+int lmkd_connect();
+
+/*
+ * Registers a process with lmkd and sets its oomadj score.
+ * On success returns 0.
+ * On error, -1 is returned.
+ * In the case of error errno is set appropriately.
+ */
+int lmkd_register_proc(int sock, struct lmk_procprio *params);
+
+/*
+ * Creates memcg directory for given process.
+ * On success returns 0.
+ * -1 is returned if path creation failed.
+ * -2 is returned if tasks file open operation failed.
+ * -3 is returned if tasks file write operation failed.
+ * In the case of error errno is set appropriately.
+ */
+int create_memcg(uid_t uid, pid_t pid);
+
+__END_DECLS
+
+#endif /* _LIBLMKD_UTILS_H_ */
diff --git a/lmkd/include/lmkd.h b/lmkd/include/lmkd.h
new file mode 100644
index 0000000..fe6364d
--- /dev/null
+++ b/lmkd/include/lmkd.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2018 Google, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LMKD_H_
+#define _LMKD_H_
+
+#include <arpa/inet.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+/*
+ * Supported LMKD commands
+ */
+enum lmk_cmd {
+ LMK_TARGET = 0, /* Associate minfree with oom_adj_score */
+ LMK_PROCPRIO, /* Register a process and set its oom_adj_score */
+ LMK_PROCREMOVE, /* Unregister a process */
+};
+
+/*
+ * Max number of targets in LMK_TARGET command.
+ */
+#define MAX_TARGETS 6
+
+/*
+ * Max packet length in bytes.
+ * Longest packet is LMK_TARGET followed by MAX_TARGETS
+ * of minfree and oom_adj_score values
+ */
+#define CTRL_PACKET_MAX_SIZE (sizeof(int) * (MAX_TARGETS * 2 + 1))
+
+/* LMKD packet - first int is lmk_cmd followed by payload */
+typedef int LMKD_CTRL_PACKET[CTRL_PACKET_MAX_SIZE / sizeof(int)];
+
+/* Get LMKD packet command */
+inline enum lmk_cmd lmkd_pack_get_cmd(LMKD_CTRL_PACKET pack) {
+ return (enum lmk_cmd)ntohl(pack[0]);
+}
+
+/* LMK_TARGET packet payload */
+struct lmk_target {
+ int minfree;
+ int oom_adj_score;
+};
+
+/*
+ * For LMK_TARGET packet get target_idx-th payload.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline void lmkd_pack_get_target(LMKD_CTRL_PACKET packet,
+ int target_idx, struct lmk_target *target) {
+ target->minfree = ntohl(packet[target_idx * 2 + 1]);
+ target->oom_adj_score = ntohl(packet[target_idx * 2 + 2]);
+}
+
+/*
+ * Prepare LMK_TARGET packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_target(LMKD_CTRL_PACKET packet,
+ struct lmk_target *targets,
+ size_t target_cnt) {
+ int idx = 0;
+ packet[idx++] = htonl(LMK_TARGET);
+ while (target_cnt) {
+ packet[idx++] = htonl(targets->minfree);
+ packet[idx++] = htonl(targets->oom_adj_score);
+ targets++;
+ target_cnt--;
+ }
+ return idx * sizeof(int);
+}
+
+/* LMK_PROCPRIO packet payload */
+struct lmk_procprio {
+ pid_t pid;
+ uid_t uid;
+ int oomadj;
+};
+
+/*
+ * For LMK_PROCPRIO packet get its payload.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline void lmkd_pack_get_procprio(LMKD_CTRL_PACKET packet,
+ struct lmk_procprio *params) {
+ params->pid = (pid_t)ntohl(packet[1]);
+ params->uid = (uid_t)ntohl(packet[2]);
+ params->oomadj = ntohl(packet[3]);
+}
+
+/*
+ * Prepare LMK_PROCPRIO packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_procprio(LMKD_CTRL_PACKET packet,
+ struct lmk_procprio *params) {
+ packet[0] = htonl(LMK_PROCPRIO);
+ packet[1] = htonl(params->pid);
+ packet[2] = htonl(params->uid);
+ packet[3] = htonl(params->oomadj);
+ return 4 * sizeof(int);
+}
+
+/* LMK_PROCREMOVE packet payload */
+struct lmk_procremove {
+ pid_t pid;
+};
+
+/*
+ * For LMK_PROCREMOVE packet get its payload.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline void lmkd_pack_get_procremove(LMKD_CTRL_PACKET packet,
+ struct lmk_procremove *params) {
+ params->pid = (pid_t)ntohl(packet[1]);
+}
+
+/*
+ * Prepare LMK_PROCREMOVE packet and return packet size in bytes.
+ * Warning: no checks performed, caller should ensure valid parameters.
+ */
+inline size_t lmkd_pack_set_procremove(LMKD_CTRL_PACKET packet,
+ struct lmk_procprio *params) {
+ packet[0] = htonl(LMK_PROCREMOVE);
+ packet[1] = htonl(params->pid);
+ return 2 * sizeof(int);
+}
+
+__END_DECLS
+
+#endif /* _LMKD_H_ */
diff --git a/lmkd/liblmkd_utils.c b/lmkd/liblmkd_utils.c
new file mode 100644
index 0000000..fa3b7a9
--- /dev/null
+++ b/lmkd/liblmkd_utils.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2018 Google, Inc
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/cdefs.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <liblmkd_utils.h>
+#include <cutils/sockets.h>
+
+int lmkd_connect() {
+ return socket_local_client("lmkd",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_SEQPACKET);
+}
+
+int lmkd_register_proc(int sock, struct lmk_procprio *params) {
+ LMKD_CTRL_PACKET packet;
+ size_t size;
+ int ret;
+
+ size = lmkd_pack_set_procprio(packet, params);
+ ret = TEMP_FAILURE_RETRY(write(sock, packet, size));
+
+ return (ret < 0) ? -1 : 0;
+}
+
+int create_memcg(uid_t uid, pid_t pid) {
+ char buf[256];
+ int tasks_file;
+ int written;
+
+ snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u", uid);
+ if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
+ errno != EEXIST) {
+ return -1;
+ }
+
+ snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u", uid, pid);
+ if (mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0 &&
+ errno != EEXIST) {
+ return -1;
+ }
+
+ snprintf(buf, sizeof(buf), "/dev/memcg/apps/uid_%u/pid_%u/tasks", uid, pid);
+ tasks_file = open(buf, O_WRONLY);
+ if (tasks_file < 0) {
+ return -2;
+ }
+ written = snprintf(buf, sizeof(buf), "%u", pid);
+ if (__predict_false(written >= (int)sizeof(buf))) {
+ written = sizeof(buf) - 1;
+ }
+ written = TEMP_FAILURE_RETRY(write(tasks_file, buf, written));
+ close(tasks_file);
+
+ return (written < 0) ? -3 : 0;
+}
+
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 338e5fa..72f9f7b 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -16,11 +16,12 @@
#define LOG_TAG "lowmemorykiller"
-#include <arpa/inet.h>
#include <errno.h>
#include <inttypes.h>
+#include <pwd.h>
#include <sched.h>
#include <signal.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/cdefs.h>
@@ -34,6 +35,7 @@
#include <cutils/properties.h>
#include <cutils/sockets.h>
+#include <lmkd.h>
#include <log/log.h>
/*
@@ -71,18 +73,8 @@
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
#define EIGHT_MEGA (1 << 23)
-enum lmk_cmd {
- LMK_TARGET,
- LMK_PROCPRIO,
- LMK_PROCREMOVE,
-};
-
-#define MAX_TARGETS 6
-/*
- * longest is LMK_TARGET followed by MAX_TARGETS each minfree and minkillprio
- * values
- */
-#define CTRL_PACKET_MAX (sizeof(int) * (MAX_TARGETS * 2 + 1))
+/* Defined as ProcessList.SYSTEM_ADJ in ProcessList.java */
+#define SYSTEM_ADJ (-900)
/* default to old in-kernel interface if no memory pressure events */
static int use_inkernel_interface = 1;
@@ -122,13 +114,30 @@
static bool kill_heaviest_task;
static unsigned long kill_timeout_ms;
-/* control socket listen and data */
-static int ctrl_lfd;
-static int ctrl_dfd = -1;
-static int ctrl_dfd_reopened; /* did we reopen ctrl conn on this loop? */
+/* data required to handle events */
+struct event_handler_info {
+ int data;
+ void (*handler)(int data, uint32_t events);
+};
-/* 3 memory pressure levels, 1 ctrl listen socket, 1 ctrl data socket */
-#define MAX_EPOLL_EVENTS 5
+/* data required to handle socket events */
+struct sock_event_handler_info {
+ int sock;
+ struct event_handler_info handler_info;
+};
+
+/* max supported number of data connections */
+#define MAX_DATA_CONN 2
+
+/* socket event handler data */
+static struct sock_event_handler_info ctrl_sock;
+static struct sock_event_handler_info data_sock[MAX_DATA_CONN];
+
+/* vmpressure event handler data */
+static struct event_handler_info vmpressure_hinfo[VMPRESS_LEVEL_COUNT];
+
+/* 3 memory pressure levels, 1 ctrl listen socket, 2 ctrl data socket */
+#define MAX_EPOLL_EVENTS (1 + MAX_DATA_CONN + VMPRESS_LEVEL_COUNT)
static int epollfd;
static int maxevents;
@@ -263,65 +272,84 @@
return 0;
}
-static void writefilestring(const char *path, char *s) {
+/*
+ * Write a string to a file.
+ * Returns false if the file does not exist.
+ */
+static bool writefilestring(const char *path, const char *s,
+ bool err_if_missing) {
int fd = open(path, O_WRONLY | O_CLOEXEC);
- int len = strlen(s);
- int ret;
+ ssize_t len = strlen(s);
+ ssize_t ret;
if (fd < 0) {
- ALOGE("Error opening %s; errno=%d", path, errno);
- return;
+ if (err_if_missing) {
+ ALOGE("Error opening %s; errno=%d", path, errno);
+ }
+ return false;
}
- ret = write(fd, s, len);
+ ret = TEMP_FAILURE_RETRY(write(fd, s, len));
if (ret < 0) {
ALOGE("Error writing %s; errno=%d", path, errno);
} else if (ret < len) {
- ALOGE("Short write on %s; length=%d", path, ret);
+ ALOGE("Short write on %s; length=%zd", path, ret);
}
close(fd);
+ return true;
}
-static void cmd_procprio(int pid, int uid, int oomadj) {
+static void cmd_procprio(LMKD_CTRL_PACKET packet) {
struct proc *procp;
char path[80];
char val[20];
int soft_limit_mult;
+ struct lmk_procprio params;
+ bool is_system_server;
+ struct passwd *pwdrec;
- if (oomadj < OOM_SCORE_ADJ_MIN || oomadj > OOM_SCORE_ADJ_MAX) {
- ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj);
+ lmkd_pack_get_procprio(packet, ¶ms);
+
+ if (params.oomadj < OOM_SCORE_ADJ_MIN ||
+ params.oomadj > OOM_SCORE_ADJ_MAX) {
+ ALOGE("Invalid PROCPRIO oomadj argument %d", params.oomadj);
return;
}
- snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid);
- snprintf(val, sizeof(val), "%d", oomadj);
- writefilestring(path, val);
+ snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", params.pid);
+ snprintf(val, sizeof(val), "%d", params.oomadj);
+ if (!writefilestring(path, val, false)) {
+ ALOGW("Failed to open %s; errno=%d: process %d might have been killed",
+ path, errno, params.pid);
+ /* If this file does not exist the process is dead. */
+ return;
+ }
if (use_inkernel_interface)
return;
- if (oomadj >= 900) {
+ if (params.oomadj >= 900) {
soft_limit_mult = 0;
- } else if (oomadj >= 800) {
+ } else if (params.oomadj >= 800) {
soft_limit_mult = 0;
- } else if (oomadj >= 700) {
+ } else if (params.oomadj >= 700) {
soft_limit_mult = 0;
- } else if (oomadj >= 600) {
+ } else if (params.oomadj >= 600) {
// Launcher should be perceptible, don't kill it.
- oomadj = 200;
+ params.oomadj = 200;
soft_limit_mult = 1;
- } else if (oomadj >= 500) {
+ } else if (params.oomadj >= 500) {
soft_limit_mult = 0;
- } else if (oomadj >= 400) {
+ } else if (params.oomadj >= 400) {
soft_limit_mult = 0;
- } else if (oomadj >= 300) {
+ } else if (params.oomadj >= 300) {
soft_limit_mult = 1;
- } else if (oomadj >= 200) {
+ } else if (params.oomadj >= 200) {
soft_limit_mult = 2;
- } else if (oomadj >= 100) {
+ } else if (params.oomadj >= 100) {
soft_limit_mult = 10;
- } else if (oomadj >= 0) {
+ } else if (params.oomadj >= 0) {
soft_limit_mult = 20;
} else {
// Persistent processes will have a large
@@ -329,11 +357,21 @@
soft_limit_mult = 64;
}
- snprintf(path, sizeof(path), "/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes", uid, pid);
+ snprintf(path, sizeof(path),
+ "/dev/memcg/apps/uid_%d/pid_%d/memory.soft_limit_in_bytes",
+ params.uid, params.pid);
snprintf(val, sizeof(val), "%d", soft_limit_mult * EIGHT_MEGA);
- writefilestring(path, val);
- procp = pid_lookup(pid);
+ /*
+ * system_server process has no memcg under /dev/memcg/apps but should be
+ * registered with lmkd. This is the best way so far to identify it.
+ */
+ is_system_server = (params.oomadj == SYSTEM_ADJ &&
+ (pwdrec = getpwnam("system")) != NULL &&
+ params.uid == pwdrec->pw_uid);
+ writefilestring(path, val, !is_system_server);
+
+ procp = pid_lookup(params.pid);
if (!procp) {
procp = malloc(sizeof(struct proc));
if (!procp) {
@@ -341,33 +379,38 @@
return;
}
- procp->pid = pid;
- procp->uid = uid;
- procp->oomadj = oomadj;
+ procp->pid = params.pid;
+ procp->uid = params.uid;
+ procp->oomadj = params.oomadj;
proc_insert(procp);
} else {
proc_unslot(procp);
- procp->oomadj = oomadj;
+ procp->oomadj = params.oomadj;
proc_slot(procp);
}
}
-static void cmd_procremove(int pid) {
+static void cmd_procremove(LMKD_CTRL_PACKET packet) {
+ struct lmk_procremove params;
+
if (use_inkernel_interface)
return;
- pid_remove(pid);
+ lmkd_pack_get_procremove(packet, ¶ms);
+ pid_remove(params.pid);
}
-static void cmd_target(int ntargets, int *params) {
+static void cmd_target(int ntargets, LMKD_CTRL_PACKET packet) {
int i;
+ struct lmk_target target;
if (ntargets > (int)ARRAY_SIZE(lowmem_adj))
return;
for (i = 0; i < ntargets; i++) {
- lowmem_minfree[i] = ntohl(*params++);
- lowmem_adj[i] = ntohl(*params++);
+ lmkd_pack_get_target(packet, i, &target);
+ lowmem_minfree[i] = target.minfree;
+ lowmem_adj[i] = target.oom_adj_score;
}
lowmem_targets_size = ntargets;
@@ -393,22 +436,29 @@
strlcat(killpriostr, val, sizeof(killpriostr));
}
- writefilestring(INKERNEL_MINFREE_PATH, minfreestr);
- writefilestring(INKERNEL_ADJ_PATH, killpriostr);
+ writefilestring(INKERNEL_MINFREE_PATH, minfreestr, true);
+ writefilestring(INKERNEL_ADJ_PATH, killpriostr, true);
}
}
-static void ctrl_data_close(void) {
- ALOGI("Closing Activity Manager data connection");
- close(ctrl_dfd);
- ctrl_dfd = -1;
+static void ctrl_data_close(int dsock_idx) {
+ struct epoll_event epev;
+
+ ALOGI("closing lmkd data connection");
+ if (epoll_ctl(epollfd, EPOLL_CTL_DEL, data_sock[dsock_idx].sock, &epev) == -1) {
+ // Log a warning and keep going
+ ALOGW("epoll_ctl for data connection socket failed; errno=%d", errno);
+ }
maxevents--;
+
+ close(data_sock[dsock_idx].sock);
+ data_sock[dsock_idx].sock = -1;
}
-static int ctrl_data_read(char *buf, size_t bufsz) {
+static int ctrl_data_read(int dsock_idx, char *buf, size_t bufsz) {
int ret = 0;
- ret = read(ctrl_dfd, buf, bufsz);
+ ret = read(data_sock[dsock_idx].sock, buf, bufsz);
if (ret == -1) {
ALOGE("control data socket read failed; errno=%d", errno);
@@ -420,39 +470,43 @@
return ret;
}
-static void ctrl_command_handler(void) {
- int ibuf[CTRL_PACKET_MAX / sizeof(int)];
+static void ctrl_command_handler(int dsock_idx) {
+ LMKD_CTRL_PACKET packet;
int len;
- int cmd = -1;
+ enum lmk_cmd cmd;
int nargs;
int targets;
- len = ctrl_data_read((char *)ibuf, CTRL_PACKET_MAX);
+ len = ctrl_data_read(dsock_idx, (char *)packet, CTRL_PACKET_MAX_SIZE);
if (len <= 0)
return;
+ if (len < (int)sizeof(int)) {
+ ALOGE("Wrong control socket read length len=%d", len);
+ return;
+ }
+
+ cmd = lmkd_pack_get_cmd(packet);
nargs = len / sizeof(int) - 1;
if (nargs < 0)
goto wronglen;
- cmd = ntohl(ibuf[0]);
-
switch(cmd) {
case LMK_TARGET:
targets = nargs / 2;
if (nargs & 0x1 || targets > (int)ARRAY_SIZE(lowmem_adj))
goto wronglen;
- cmd_target(targets, &ibuf[1]);
+ cmd_target(targets, packet);
break;
case LMK_PROCPRIO:
if (nargs != 3)
goto wronglen;
- cmd_procprio(ntohl(ibuf[1]), ntohl(ibuf[2]), ntohl(ibuf[3]));
+ cmd_procprio(packet);
break;
case LMK_PROCREMOVE:
if (nargs != 1)
goto wronglen;
- cmd_procremove(ntohl(ibuf[1]));
+ cmd_procremove(packet);
break;
default:
ALOGE("Received unknown command code %d", cmd);
@@ -465,40 +519,57 @@
ALOGE("Wrong control socket read length cmd=%d len=%d", cmd, len);
}
-static void ctrl_data_handler(uint32_t events) {
- if (events & EPOLLHUP) {
- ALOGI("ActivityManager disconnected");
- if (!ctrl_dfd_reopened)
- ctrl_data_close();
- } else if (events & EPOLLIN) {
- ctrl_command_handler();
+static void ctrl_data_handler(int data, uint32_t events) {
+ if (events & EPOLLIN) {
+ ctrl_command_handler(data);
}
}
-static void ctrl_connect_handler(uint32_t events __unused) {
- struct epoll_event epev;
+static int get_free_dsock() {
+ for (int i = 0; i < MAX_DATA_CONN; i++) {
+ if (data_sock[i].sock < 0) {
+ return i;
+ }
+ }
+ return -1;
+}
- if (ctrl_dfd >= 0) {
- ctrl_data_close();
- ctrl_dfd_reopened = 1;
+static void ctrl_connect_handler(int data __unused, uint32_t events __unused) {
+ struct epoll_event epev;
+ int free_dscock_idx = get_free_dsock();
+
+ if (free_dscock_idx < 0) {
+ /*
+ * Number of data connections exceeded max supported. This should not
+ * happen but if it does we drop all existing connections and accept
+ * the new one. This prevents inactive connections from monopolizing
+ * data socket and if we drop ActivityManager connection it will
+ * immediately reconnect.
+ */
+ for (int i = 0; i < MAX_DATA_CONN; i++) {
+ ctrl_data_close(i);
+ }
+ free_dscock_idx = 0;
}
- ctrl_dfd = accept(ctrl_lfd, NULL, NULL);
-
- if (ctrl_dfd < 0) {
+ data_sock[free_dscock_idx].sock = accept(ctrl_sock.sock, NULL, NULL);
+ if (data_sock[free_dscock_idx].sock < 0) {
ALOGE("lmkd control socket accept failed; errno=%d", errno);
return;
}
- ALOGI("ActivityManager connected");
- maxevents++;
+ ALOGI("lmkd data connection established");
+ /* use data to store data connection idx */
+ data_sock[free_dscock_idx].handler_info.data = free_dscock_idx;
+ data_sock[free_dscock_idx].handler_info.handler = ctrl_data_handler;
epev.events = EPOLLIN;
- epev.data.ptr = (void *)ctrl_data_handler;
- if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_dfd, &epev) == -1) {
+ epev.data.ptr = (void *)&(data_sock[free_dscock_idx].handler_info);
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, data_sock[free_dscock_idx].sock, &epev) == -1) {
ALOGE("epoll_ctl for data connection socket failed; errno=%d", errno);
- ctrl_data_close();
+ ctrl_data_close(free_dscock_idx);
return;
}
+ maxevents++;
}
static int zoneinfo_parse_protection(char *cp) {
@@ -684,7 +755,7 @@
r = kill(pid, SIGKILL);
ALOGI(
"Killing '%s' (%d), uid %d, adj %d\n"
- " to free %ldkB because system is under %s memory pressure oom_adj %d\n",
+ " to free %ldkB because system is under %s memory pressure (min_oom_adj=%d)\n",
taskname, pid, uid, procp->oomadj, tasksize * page_k,
level_name[level], min_score_adj);
pid_remove(pid);
@@ -802,7 +873,7 @@
(to->tv_usec - from->tv_usec) / 1000;
}
-static void mp_event_common(enum vmpressure_level level) {
+static void mp_event_common(int data, uint32_t events __unused) {
int ret;
unsigned long long evcount;
int64_t mem_usage, memsw_usage;
@@ -811,6 +882,7 @@
struct mem_size free_mem;
static struct timeval last_report_tm;
static unsigned long skip_count = 0;
+ enum vmpressure_level level = (enum vmpressure_level)data;
/*
* Check all event counters from low to critical
@@ -927,26 +999,15 @@
}
}
-static void mp_event_low(uint32_t events __unused) {
- mp_event_common(VMPRESS_LEVEL_LOW);
-}
-
-static void mp_event_medium(uint32_t events __unused) {
- mp_event_common(VMPRESS_LEVEL_MEDIUM);
-}
-
-static void mp_event_critical(uint32_t events __unused) {
- mp_event_common(VMPRESS_LEVEL_CRITICAL);
-}
-
-static bool init_mp_common(void *event_handler, enum vmpressure_level level) {
+static bool init_mp_common(enum vmpressure_level level) {
int mpfd;
int evfd;
int evctlfd;
char buf[256];
struct epoll_event epev;
int ret;
- const char *levelstr = level_name[level];
+ int level_idx = (int)level;
+ const char *levelstr = level_name[level_idx];
mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC);
if (mpfd < 0) {
@@ -972,7 +1033,7 @@
goto err;
}
- ret = write(evctlfd, buf, strlen(buf) + 1);
+ ret = TEMP_FAILURE_RETRY(write(evctlfd, buf, strlen(buf) + 1));
if (ret == -1) {
ALOGE("cgroup.event_control write failed for level %s; errno=%d",
levelstr, errno);
@@ -980,7 +1041,10 @@
}
epev.events = EPOLLIN;
- epev.data.ptr = event_handler;
+ /* use data to store event level */
+ vmpressure_hinfo[level_idx].data = level_idx;
+ vmpressure_hinfo[level_idx].handler = mp_event_common;
+ epev.data.ptr = (void *)&vmpressure_hinfo[level_idx];
ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, evfd, &epev);
if (ret == -1) {
ALOGE("epoll_ctl for level %s failed; errno=%d", levelstr, errno);
@@ -1017,21 +1081,27 @@
return -1;
}
- ctrl_lfd = android_get_control_socket("lmkd");
- if (ctrl_lfd < 0) {
+ // mark data connections as not connected
+ for (int i = 0; i < MAX_DATA_CONN; i++) {
+ data_sock[i].sock = -1;
+ }
+
+ ctrl_sock.sock = android_get_control_socket("lmkd");
+ if (ctrl_sock.sock < 0) {
ALOGE("get lmkd control socket failed");
return -1;
}
- ret = listen(ctrl_lfd, 1);
+ ret = listen(ctrl_sock.sock, MAX_DATA_CONN);
if (ret < 0) {
ALOGE("lmkd control socket listen failed (errno=%d)", errno);
return -1;
}
epev.events = EPOLLIN;
- epev.data.ptr = (void *)ctrl_connect_handler;
- if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_lfd, &epev) == -1) {
+ ctrl_sock.handler_info.handler = ctrl_connect_handler;
+ epev.data.ptr = (void *)&(ctrl_sock.handler_info);
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ctrl_sock.sock, &epev) == -1) {
ALOGE("epoll_ctl for lmkd control socket failed (errno=%d)", errno);
return -1;
}
@@ -1043,10 +1113,9 @@
if (use_inkernel_interface) {
ALOGI("Using in-kernel low memory killer interface");
} else {
- if (!init_mp_common((void *)&mp_event_low, VMPRESS_LEVEL_LOW) ||
- !init_mp_common((void *)&mp_event_medium, VMPRESS_LEVEL_MEDIUM) ||
- !init_mp_common((void *)&mp_event_critical,
- VMPRESS_LEVEL_CRITICAL)) {
+ if (!init_mp_common(VMPRESS_LEVEL_LOW) ||
+ !init_mp_common(VMPRESS_LEVEL_MEDIUM) ||
+ !init_mp_common(VMPRESS_LEVEL_CRITICAL)) {
ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
return -1;
}
@@ -1061,12 +1130,14 @@
}
static void mainloop(void) {
+ struct event_handler_info* handler_info;
+ struct epoll_event *evt;
+
while (1) {
struct epoll_event events[maxevents];
int nevents;
int i;
- ctrl_dfd_reopened = 0;
nevents = epoll_wait(epollfd, events, maxevents, -1);
if (nevents == -1) {
@@ -1076,11 +1147,33 @@
continue;
}
- for (i = 0; i < nevents; ++i) {
- if (events[i].events & EPOLLERR)
+ /*
+ * First pass to see if any data socket connections were dropped.
+ * Dropped connection should be handled before any other events
+ * to deallocate data connection and correctly handle cases when
+ * connection gets dropped and reestablished in the same epoll cycle.
+ * In such cases it's essential to handle connection closures first.
+ */
+ for (i = 0, evt = &events[0]; i < nevents; ++i, evt++) {
+ if ((evt->events & EPOLLHUP) && evt->data.ptr) {
+ ALOGI("lmkd data connection dropped");
+ handler_info = (struct event_handler_info*)evt->data.ptr;
+ ctrl_data_close(handler_info->data);
+ }
+ }
+
+ /* Second pass to handle all other events */
+ for (i = 0, evt = &events[0]; i < nevents; ++i, evt++) {
+ if (evt->events & EPOLLERR)
ALOGD("EPOLLERR on event #%d", i);
- if (events[i].data.ptr)
- (*(void (*)(uint32_t))events[i].data.ptr)(events[i].events);
+ if (evt->events & EPOLLHUP) {
+ /* This case was handled in the first pass */
+ continue;
+ }
+ if (evt->data.ptr) {
+ handler_info = (struct event_handler_info*)evt->data.ptr;
+ handler_info->handler(handler_info->data, evt->events);
+ }
}
}
}
diff --git a/lmkd/tests/Android.bp b/lmkd/tests/Android.bp
new file mode 100644
index 0000000..cbf44e9
--- /dev/null
+++ b/lmkd/tests/Android.bp
@@ -0,0 +1,40 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+ name: "lmkd_unit_test",
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+
+ static_libs: [
+ "liblmkd_utils",
+ ],
+
+ target: {
+ android: {
+ srcs: ["lmkd_test.cpp"],
+ },
+ },
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+
+ compile_multilib: "first",
+}
diff --git a/lmkd/tests/lmkd_test.cpp b/lmkd/tests/lmkd_test.cpp
new file mode 100644
index 0000000..f17512d
--- /dev/null
+++ b/lmkd/tests/lmkd_test.cpp
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sstream>
+#include <stdio.h>
+#include <string.h>
+#include <string>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <gtest/gtest.h>
+#include <lmkd.h>
+#include <liblmkd_utils.h>
+#include <log/log_properties.h>
+#include <private/android_filesystem_config.h>
+
+using namespace android::base;
+
+#define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
+#define LMKDTEST_RESPAWN_FLAG "LMKDTEST_RESPAWN"
+
+#define LMKD_LOGCAT_MARKER "lowmemorykiller"
+#define LMKD_KILL_MARKER_TEMPLATE LMKD_LOGCAT_MARKER ": Killing '%s'"
+#define OOM_MARKER "Out of memory"
+#define OOM_KILL_MARKER "Killed process"
+#define MIN_LOG_SIZE 100
+
+#define ONE_MB (1 << 20)
+
+/* Test constant parameters */
+#define OOM_ADJ_MAX 1000
+#define OOM_ADJ_MIN 0
+#define OOM_ADJ_STEP 100
+#define STEP_COUNT ((OOM_ADJ_MAX - OOM_ADJ_MIN) / OOM_ADJ_STEP + 1)
+
+#define ALLOC_STEP (ONE_MB)
+#define ALLOC_DELAY 1000
+
+/* Utility functions */
+std::string readCommand(const std::string& command) {
+ FILE* fp = popen(command.c_str(), "r");
+ std::string content;
+ ReadFdToString(fileno(fp), &content);
+ pclose(fp);
+ return content;
+}
+
+std::string readLogcat(const std::string& marker) {
+ std::string content = readCommand("logcat -d -b all");
+ size_t pos = content.find(marker);
+ if (pos == std::string::npos) return "";
+ content.erase(0, pos);
+ return content;
+}
+
+bool writeFile(const std::string& file, const std::string& string) {
+ if (getuid() == static_cast<unsigned>(AID_ROOT)) {
+ return WriteStringToFile(string, file);
+ }
+ return string == readCommand(
+ "echo -n '" + string + "' | su root tee " + file + " 2>&1");
+}
+
+bool writeKmsg(const std::string& marker) {
+ return writeFile("/dev/kmsg", marker);
+}
+
+std::string getTextAround(const std::string& text, size_t pos,
+ size_t lines_before, size_t lines_after) {
+ size_t start_pos = pos;
+
+ // find start position
+ // move up lines_before number of lines
+ while (lines_before > 0 &&
+ (start_pos = text.rfind('\n', start_pos)) != std::string::npos) {
+ lines_before--;
+ }
+ // move to the beginning of the line
+ start_pos = text.rfind('\n', start_pos);
+ start_pos = (start_pos == std::string::npos) ? 0 : start_pos + 1;
+
+ // find end position
+ // move down lines_after number of lines
+ while (lines_after > 0 &&
+ (pos = text.find('\n', pos)) != std::string::npos) {
+ pos++;
+ lines_after--;
+ }
+ return text.substr(start_pos, (pos == std::string::npos) ?
+ std::string::npos : pos - start_pos);
+}
+
+bool getExecPath(std::string &path) {
+ // exec path as utf8z c_str().
+ // std::string contains _all_ nul terminated argv[] strings.
+ return android::base::ReadFileToString("/proc/self/cmdline", &path);
+}
+
+/* Child synchronization primitives */
+#define STATE_INIT 0
+#define STATE_CHILD_READY 1
+#define STATE_PARENT_READY 2
+
+struct state_sync {
+ pthread_mutex_t mutex;
+ pthread_cond_t condition;
+ int state;
+};
+
+struct state_sync * init_state_sync_obj() {
+ struct state_sync *ssync;
+
+ ssync = (struct state_sync*)mmap(NULL, sizeof(struct state_sync),
+ PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+ if (ssync == MAP_FAILED) {
+ return NULL;
+ }
+
+ pthread_mutexattr_t mattr;
+ pthread_mutexattr_init(&mattr);
+ pthread_mutexattr_setpshared(&mattr, PTHREAD_PROCESS_SHARED);
+ pthread_mutex_init(&ssync->mutex, &mattr);
+
+ pthread_condattr_t cattr;
+ pthread_condattr_init(&cattr);
+ pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
+ pthread_cond_init(&ssync->condition, &cattr);
+
+ ssync->state = STATE_INIT;
+ return ssync;
+}
+
+void destroy_state_sync_obj(struct state_sync *ssync) {
+ pthread_cond_destroy(&ssync->condition);
+ pthread_mutex_destroy(&ssync->mutex);
+ munmap(ssync, sizeof(struct state_sync));
+}
+
+void signal_state(struct state_sync *ssync, int state) {
+ pthread_mutex_lock(&ssync->mutex);
+ ssync->state = state;
+ pthread_cond_signal(&ssync->condition);
+ pthread_mutex_unlock(&ssync->mutex);
+}
+
+void wait_for_state(struct state_sync *ssync, int state) {
+ pthread_mutex_lock(&ssync->mutex);
+ while (ssync->state != state) {
+ pthread_cond_wait(&ssync->condition, &ssync->mutex);
+ }
+ pthread_mutex_unlock(&ssync->mutex);
+}
+
+/* Memory allocation and data sharing */
+struct shared_data {
+ size_t allocated;
+ bool finished;
+ size_t total_size;
+ size_t step_size;
+ size_t step_delay;
+ int oomadj;
+};
+
+volatile void *gptr;
+void add_pressure(struct shared_data *data) {
+ volatile void *ptr;
+ size_t allocated_size = 0;
+
+ data->finished = false;
+ while (allocated_size < data->total_size) {
+ ptr = mmap(NULL, data->step_size, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
+ if (ptr != MAP_FAILED) {
+ /* create ptr aliasing to prevent compiler optimizing the access */
+ gptr = ptr;
+ /* make data non-zero */
+ memset((void*)ptr, (int)(allocated_size + 1), data->step_size);
+ allocated_size += data->step_size;
+ data->allocated = allocated_size;
+ }
+ usleep(data->step_delay);
+ }
+ data->finished = (allocated_size >= data->total_size);
+}
+
+/* Memory stress test main body */
+void runMemStressTest() {
+ struct shared_data *data;
+ struct state_sync *ssync;
+ int sock;
+ pid_t pid;
+ uid_t uid = getuid();
+
+ ASSERT_FALSE((sock = lmkd_connect()) < 0)
+ << "Failed to connect to lmkd process, err=" << strerror(errno);
+
+ /* allocate shared memory to communicate params with a child */
+ data = (struct shared_data*)mmap(NULL, sizeof(struct shared_data),
+ PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
+ ASSERT_FALSE(data == MAP_FAILED) << "Memory allocation failure";
+ data->total_size = (size_t)-1; /* allocate until killed */
+ data->step_size = ALLOC_STEP;
+ data->step_delay = ALLOC_DELAY;
+
+ /* allocate state sync object */
+ ASSERT_FALSE((ssync = init_state_sync_obj()) == NULL)
+ << "Memory allocation failure";
+
+ /* run the test gradually decreasing oomadj */
+ data->oomadj = OOM_ADJ_MAX;
+ while (data->oomadj >= OOM_ADJ_MIN) {
+ ASSERT_FALSE((pid = fork()) < 0)
+ << "Failed to spawn a child process, err=" << strerror(errno);
+ if (pid != 0) {
+ /* Parent */
+ struct lmk_procprio params;
+ /* wait for child to start and get ready */
+ wait_for_state(ssync, STATE_CHILD_READY);
+ params.pid = pid;
+ params.uid = uid;
+ params.oomadj = data->oomadj;
+ ASSERT_FALSE(lmkd_register_proc(sock, ¶ms) < 0)
+ << "Failed to communicate with lmkd, err=" << strerror(errno);
+ // signal the child it can proceed
+ signal_state(ssync, STATE_PARENT_READY);
+ waitpid(pid, NULL, 0);
+ if (data->finished) {
+ GTEST_LOG_(INFO) << "Child [pid=" << pid << "] allocated "
+ << data->allocated / ONE_MB << "MB";
+ } else {
+ GTEST_LOG_(INFO) << "Child [pid=" << pid << "] allocated "
+ << data->allocated / ONE_MB
+ << "MB before being killed";
+ }
+ data->oomadj -= OOM_ADJ_STEP;
+ } else {
+ /* Child */
+ pid = getpid();
+ GTEST_LOG_(INFO) << "Child [pid=" << pid
+ << "] is running at oomadj="
+ << data->oomadj;
+ data->allocated = 0;
+ data->finished = false;
+ ASSERT_FALSE(create_memcg(uid, pid) != 0)
+ << "Child [pid=" << pid << "] failed to create a cgroup";
+ signal_state(ssync, STATE_CHILD_READY);
+ wait_for_state(ssync, STATE_PARENT_READY);
+ add_pressure(data);
+ /* should not reach here, child should be killed by OOM/LMK */
+ FAIL() << "Child [pid=" << pid << "] was not killed";
+ break;
+ }
+ }
+ destroy_state_sync_obj(ssync);
+ munmap(data, sizeof(struct shared_data));
+ close(sock);
+}
+
+TEST(lmkd, check_for_oom) {
+ // test requirements
+ // userdebug build
+ if (!__android_log_is_debuggable()) {
+ GTEST_LOG_(INFO) << "Must be userdebug build, terminating test";
+ return;
+ }
+ // check if in-kernel LMK driver is present
+ if (!access(INKERNEL_MINFREE_PATH, W_OK)) {
+ GTEST_LOG_(INFO) << "Must not have kernel lowmemorykiller driver,"
+ << " terminating test";
+ return;
+ }
+
+ // if respawned test process then run the test and exit (no analysis)
+ if (getenv(LMKDTEST_RESPAWN_FLAG) != NULL) {
+ runMemStressTest();
+ return;
+ }
+
+ // Main test process
+ // mark the beginning of the test
+ std::string marker = StringPrintf(
+ "LMKD test start %lu\n", static_cast<unsigned long>(time(nullptr)));
+ ASSERT_TRUE(writeKmsg(marker));
+
+ // get executable complete path
+ std::string test_path;
+ ASSERT_TRUE(getExecPath(test_path));
+
+ std::string test_output;
+ if (getuid() != static_cast<unsigned>(AID_ROOT)) {
+ // if not root respawn itself as root and capture output
+ std::string command = StringPrintf(
+ "%s=true su root %s --gtest_filter=lmkd.check_for_oom 2>&1",
+ LMKDTEST_RESPAWN_FLAG, test_path.c_str());
+ std::string test_output = readCommand(command);
+ GTEST_LOG_(INFO) << test_output;
+ } else {
+ // main test process is root, run the test
+ runMemStressTest();
+ }
+
+ // Analyze results
+ // capture logcat containind kernel logs
+ std::string logcat_out = readLogcat(marker);
+
+ // 1. extract LMKD kills from logcat output, count kills
+ std::stringstream kill_logs;
+ int hit_count = 0;
+ size_t pos = 0;
+ marker = StringPrintf(LMKD_KILL_MARKER_TEMPLATE, test_path.c_str());
+
+ while (true) {
+ if ((pos = logcat_out.find(marker, pos)) != std::string::npos) {
+ kill_logs << getTextAround(logcat_out, pos, 0, 1);
+ pos += marker.length();
+ hit_count++;
+ } else {
+ break;
+ }
+ }
+ GTEST_LOG_(INFO) << "====Logged kills====" << std::endl
+ << kill_logs.str();
+ EXPECT_TRUE(hit_count == STEP_COUNT) << "Number of kills " << hit_count
+ << " is less than expected "
+ << STEP_COUNT;
+
+ // 2. check kernel logs for OOM kills
+ pos = logcat_out.find(OOM_MARKER);
+ bool oom_detected = (pos != std::string::npos);
+ bool oom_kill_detected = (oom_detected &&
+ logcat_out.find(OOM_KILL_MARKER, pos) != std::string::npos);
+
+ EXPECT_FALSE(oom_kill_detected) << "OOM kill is detected!";
+ if (oom_detected || oom_kill_detected) {
+ // capture logcat with logs around all OOMs
+ pos = 0;
+ while ((pos = logcat_out.find(OOM_MARKER, pos)) != std::string::npos) {
+ GTEST_LOG_(INFO) << "====Logs around OOM====" << std::endl
+ << getTextAround(logcat_out, pos,
+ MIN_LOG_SIZE / 2, MIN_LOG_SIZE / 2);
+ pos += strlen(OOM_MARKER);
+ }
+ }
+
+ // output complete logcat with kernel (might get truncated)
+ GTEST_LOG_(INFO) << "====Complete logcat output====" << std::endl
+ << logcat_out;
+}
+
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index b8af2f0..a78319f 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -171,7 +171,9 @@
}
// audit message (except sequence number) identical?
- if (last->isBinary()) {
+ if (last->isBinary() &&
+ (lenl > static_cast<ssize_t>(sizeof(android_log_event_string_t))) &&
+ (lenr > static_cast<ssize_t>(sizeof(android_log_event_string_t)))) {
if (fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_string_t) -
sizeof(int32_t))) {
return DIFFERENT;
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index feb100e..f488ed5 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -93,6 +93,23 @@
else
LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product $(TARGET_ROOT_OUT)/product
endif
+
+# For /odm partition.
+LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm
+# For Treble Generic System Image (GSI), system-as-root GSI needs to work on
+# both devices with and without /odm partition. Those symlinks are for devices
+# without /odm partition. For devices with /odm partition, mount odm.img under
+# /odm will hide those symlinks.
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/app $(TARGET_ROOT_OUT)/odm/app
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/bin $(TARGET_ROOT_OUT)/odm/bin
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/etc $(TARGET_ROOT_OUT)/odm/etc
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/firmware $(TARGET_ROOT_OUT)/odm/firmware
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/framework $(TARGET_ROOT_OUT)/odm/framework
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/lib $(TARGET_ROOT_OUT)/odm/lib
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/lib64 $(TARGET_ROOT_OUT)/odm/lib64
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/overlay $(TARGET_ROOT_OUT)/odm/overlay
+LOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/priv-app $(TARGET_ROOT_OUT)/odm/priv-app
+
ifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache
else
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index d55ec57..ca6aafe 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -10,6 +10,9 @@
dir.legacy = /odm
dir.legacy = /sbin
+# Except for /postinstall, where only /system is searched
+dir.postinstall = /postinstall
+
[legacy]
namespace.default.isolated = false
@@ -23,3 +26,15 @@
namespace.default.asan.search.paths += /odm/${LIB}
namespace.default.asan.search.paths += /data/asan/vendor/${LIB}
namespace.default.asan.search.paths += /vendor/${LIB}
+
+###############################################################################
+# Namespace config for binaries under /postinstall.
+# Only one default namespace is defined and it has no directories other than
+# /system/lib in the search paths. This is because linker calls realpath on the
+# search paths and this causes selinux denial if the paths (/vendor, /odm) are
+# not allowed to the poinstall binaries. There is no reason to allow the
+# binaries to access the paths.
+###############################################################################
+[postinstall]
+namespace.default.isolated = false
+namespace.default.search.paths = /system/${LIB}
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index c8d87c8..94465f4 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -24,6 +24,8 @@
dir.system = /data/benchmarktest
dir.system = /data/benchmarktest64
+dir.postinstall = /postinstall
+
[system]
additional.namespaces = sphal,vndk,rs
@@ -54,6 +56,9 @@
namespace.default.permitted.paths += /vendor/framework
namespace.default.permitted.paths += /vendor/app
namespace.default.permitted.paths += /vendor/priv-app
+namespace.default.permitted.paths += /odm/framework
+namespace.default.permitted.paths += /odm/app
+namespace.default.permitted.paths += /odm/priv-app
namespace.default.permitted.paths += /oem/app
namespace.default.permitted.paths += /product/framework
namespace.default.permitted.paths += /product/app
@@ -74,6 +79,9 @@
namespace.default.asan.permitted.paths += /vendor/framework
namespace.default.asan.permitted.paths += /vendor/app
namespace.default.asan.permitted.paths += /vendor/priv-app
+namespace.default.asan.permitted.paths += /odm/framework
+namespace.default.asan.permitted.paths += /odm/app
+namespace.default.asan.permitted.paths += /odm/priv-app
namespace.default.asan.permitted.paths += /oem/app
namespace.default.asan.permitted.paths += /product/framework
namespace.default.asan.permitted.paths += /product/app
@@ -309,3 +317,15 @@
namespace.system.asan.search.paths = /data/asan/system/${LIB}
namespace.system.asan.search.paths += /system/${LIB}
+
+###############################################################################
+# Namespace config for binaries under /postinstall.
+# Only one default namespace is defined and it has no directories other than
+# /system/lib in the search paths. This is because linker calls realpath on the
+# search paths and this causes selinux denial if the paths (/vendor, /odm) are
+# not allowed to the poinstall binaries. There is no reason to allow the
+# binaries to access the paths.
+###############################################################################
+[postinstall]
+namespace.default.isolated = false
+namespace.default.search.paths = /system/${LIB}
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index 5256cb1..1fd4195 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -24,6 +24,8 @@
dir.system = /data/benchmarktest
dir.system = /data/benchmarktest64
+dir.postinstall = /postinstall
+
[system]
additional.namespaces = sphal,vndk,rs
@@ -222,3 +224,15 @@
namespace.default.asan.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.default.asan.search.paths += /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
+
+###############################################################################
+# Namespace config for binaries under /postinstall.
+# Only one default namespace is defined and it has no directories other than
+# /system/lib in the search paths. This is because linker calls realpath on the
+# search paths and this causes selinux denial if the paths (/vendor, /odm) are
+# not allowed to the poinstall binaries. There is no reason to allow the
+# binaries to access the paths.
+###############################################################################
+[postinstall]
+namespace.default.isolated = false
+namespace.default.search.paths = /system/${LIB}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index a213ffb..1462570 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -573,6 +573,9 @@
hostname localhost
domainname localdomain
+ # IPsec SA default expiration length
+ write /proc/sys/net/core/xfrm_acq_expires 3600
+
# Memory management. Basic kernel parameters, and allow the high
# level system server to be able to adjust the kernel OOM driver
# parameters to match how it is managing things.
diff --git a/storaged/OWNERS b/storaged/OWNERS
index 7445270..c6feee8 100644
--- a/storaged/OWNERS
+++ b/storaged/OWNERS
@@ -1 +1,2 @@
jinqian@google.com
+salyzyn@google.com