Merge "adbd: spawn login shell when run without a command."
diff --git a/adb/Android.bp b/adb/Android.bp
index bbf7cb4..d81bb4b 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -72,8 +72,17 @@
"-DUNICODE=1",
"-D_UNICODE=1",
- // -std=gnu++14 doesn't set _GNU_SOURCE on Windows.
+ // -std=gnu++11 doesn't set _GNU_SOURCE on Windows.
"-D_GNU_SOURCE",
+
+ // MinGW hides some things behind _POSIX_SOURCE.
+ "-D_POSIX_SOURCE",
+ ],
+
+ host_ldlibs: [
+ "-lws2_32",
+ "-lgdi32",
+ "-luserenv",
],
},
},
@@ -173,6 +182,13 @@
"libdiagnose_usb",
"libusb",
],
+
+ target: {
+ windows: {
+ enabled: true,
+ shared_libs: ["AdbWinApi"],
+ },
+ },
}
cc_binary_host {
@@ -220,11 +236,6 @@
windows: {
enabled: true,
ldflags: ["-municode"],
- host_ldlibs: [
- "-lws2_32",
- "-lgdi32",
- ],
-
shared_libs: ["AdbWinApi"],
required: [
"AdbWinUsbApi",
diff --git a/adb/Android.mk b/adb/Android.mk
new file mode 100644
index 0000000..8b2d558
--- /dev/null
+++ b/adb/Android.mk
@@ -0,0 +1,8 @@
+LOCAL_PATH := $(call my-dir)
+
+# Archive adb, adb.exe.
+$(call dist-for-goals,dist_files sdk win_sdk,$(HOST_OUT_EXECUTABLES)/adb)
+
+ifdef HOST_CROSS_OS
+$(call dist-for-goals,dist_files sdk win_sdk,$(ALL_MODULES.host_cross_adb.BUILT))
+endif
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/daemon/shell_service.cpp b/adb/daemon/shell_service.cpp
index bb65977..401c99c 100644
--- a/adb/daemon/shell_service.cpp
+++ b/adb/daemon/shell_service.cpp
@@ -334,6 +334,15 @@
// processes, so we need to manually reset back to SIG_DFL here (http://b/35209888).
signal(SIGPIPE, SIG_DFL);
+ // Increase oom_score_adj from -1000, so that the child is visible to the OOM-killer.
+ // Don't treat failure as an error, because old Android kernels explicitly disabled this.
+ int oom_score_adj_fd = adb_open("/proc/self/oom_score_adj", O_WRONLY | O_CLOEXEC);
+ if (oom_score_adj_fd != -1) {
+ const char* oom_score_adj_value = "-950";
+ TEMP_FAILURE_RETRY(
+ adb_write(oom_score_adj_fd, oom_score_adj_value, strlen(oom_score_adj_value)));
+ }
+
if (command_.empty()) {
// Spawn a login shell if we don't have a command.
execle(_PATH_BSHELL, "-" _PATH_BSHELL, nullptr, cenv.data());
@@ -682,22 +691,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 c58c67a..ab11bdd 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -269,7 +269,7 @@
if (h->control < 0) { // might have already done this before
LOG(INFO) << "opening control endpoint " << USB_FFS_ADB_EP0;
- h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR);
+ h->control = adb_open(USB_FFS_ADB_EP0, O_WRONLY);
if (h->control < 0) {
PLOG(ERROR) << "cannot open control endpoint " << USB_FFS_ADB_EP0;
goto err;
@@ -300,13 +300,13 @@
android::base::SetProperty("sys.usb.ffs.ready", "1");
}
- h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDWR);
+ h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDONLY);
if (h->bulk_out < 0) {
PLOG(ERROR) << "cannot open bulk-out endpoint " << USB_FFS_ADB_OUT;
goto err;
}
- h->bulk_in = adb_open(USB_FFS_ADB_IN, O_RDWR);
+ h->bulk_in = adb_open(USB_FFS_ADB_IN, O_WRONLY);
if (h->bulk_in < 0) {
PLOG(ERROR) << "cannot open bulk-in endpoint " << USB_FFS_ADB_IN;
goto err;
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index ba82e7c..9776c1b 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -44,13 +44,6 @@
#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
@@ -296,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.
@@ -402,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);
@@ -418,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";
}
}
@@ -426,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 dadae5a..e3d5a35 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -26,6 +26,7 @@
#include "adb_io.h"
#include "fdevent_test.h"
+#include "sysdeps/memory.h"
class FdHandler {
public:
@@ -180,7 +181,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 +196,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 44d3276..6b40056 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -209,7 +209,6 @@
TerminateThread(thread);
}
-#if 0
// 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];
@@ -248,7 +247,6 @@
ASSERT_EQ(GetAdditionalLocalSocketCount(), fdevent_installed_count());
TerminateThread(thread);
}
-#endif
#if defined(__linux__)
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 fd08ad8..3be99f6 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -106,14 +106,14 @@
#define mkdir ___xxx_mkdir
// See the comments for the !defined(_WIN32) versions of adb_*().
-extern int adb_open(const char* path, int options);
-extern int adb_creat(const char* path, int mode);
-extern int adb_read(int fd, void* buf, int len);
-extern int adb_write(int fd, const void* buf, int len);
-extern int adb_lseek(int fd, int pos, int where);
-extern int adb_shutdown(int fd);
-extern int adb_close(int fd);
-extern int adb_register_socket(SOCKET s);
+extern int adb_open(const char* path, int options);
+extern int adb_creat(const char* path, int mode);
+extern int adb_read(int fd, void* buf, int len);
+extern int adb_write(int fd, const void* buf, int len);
+extern int adb_lseek(int fd, int pos, int where);
+extern int adb_shutdown(int fd, int direction = SHUT_RDWR);
+extern int adb_close(int fd);
+extern int adb_register_socket(SOCKET s);
// See the comments for the !defined(_WIN32) version of unix_close().
static __inline__ int unix_close(int fd)
@@ -414,14 +414,10 @@
#undef open
#define open ___xxx_open
-static __inline__ int adb_shutdown(int fd)
-{
- return shutdown(fd, SHUT_RDWR);
-}
-static __inline__ int adb_shutdown(int fd, int direction)
-{
+static __inline__ int adb_shutdown(int fd, int direction = SHUT_RDWR) {
return shutdown(fd, direction);
}
+
#undef shutdown
#define shutdown ____xxx_shutdown
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..62f4ac8 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());
@@ -1013,9 +1011,8 @@
return ntohs(reinterpret_cast<sockaddr_in*>(&addr_storage)->sin_port);
}
-int adb_shutdown(int fd)
-{
- FH f = _fh_from_int(fd, __func__);
+int adb_shutdown(int fd, int direction) {
+ FH f = _fh_from_int(fd, __func__);
if (!f || f->clazz != &_fh_socket_class) {
D("adb_shutdown: invalid fd %d", fd);
@@ -1023,8 +1020,8 @@
return -1;
}
- D( "adb_shutdown: %s", f->name);
- if (shutdown(f->fh_socket, SD_BOTH) == SOCKET_ERROR) {
+ D("adb_shutdown: %s", f->name);
+ if (shutdown(f->fh_socket, direction) == SOCKET_ERROR) {
const DWORD err = WSAGetLastError();
D("socket shutdown fd %d failed: %s", fd,
android::base::SystemErrorCodeToString(err).c_str());
diff --git a/adb/test_device.py b/adb/test_device.py
index d422df2..d39eb14 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -1187,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/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
index abcf4bc..c314e02 100644
--- a/base/include/android-base/scopeguard.h
+++ b/base/include/android-base/scopeguard.h
@@ -17,20 +17,27 @@
#ifndef ANDROID_BASE_SCOPEGUARD_H
#define ANDROID_BASE_SCOPEGUARD_H
-#include <utility> // for std::move
+#include <utility> // for std::move, std::forward
namespace android {
namespace base {
+// ScopeGuard ensures that the specified functor is executed no matter how the
+// current scope exits.
template <typename F>
class ScopeGuard {
public:
- ScopeGuard(F f) : f_(f), active_(true) {}
+ ScopeGuard(F&& f) : f_(std::forward<F>(f)), active_(true) {}
ScopeGuard(ScopeGuard&& that) : f_(std::move(that.f_)), active_(that.active_) {
that.active_ = false;
}
+ template <typename Functor>
+ ScopeGuard(ScopeGuard<Functor>&& that) : f_(std::move(that.f_)), active_(that.active_) {
+ that.active_ = false;
+ }
+
~ScopeGuard() {
if (active_) f_();
}
@@ -45,13 +52,16 @@
bool active() const { return active_; }
private:
+ template <typename Functor>
+ friend class ScopeGuard;
+
F f_;
bool active_;
};
-template <typename T>
-ScopeGuard<T> make_scope_guard(T f) {
- return ScopeGuard<T>(f);
+template <typename F>
+ScopeGuard<F> make_scope_guard(F&& f) {
+ return ScopeGuard<F>(std::forward<F>(f));
}
} // namespace base
diff --git a/base/scopeguard_test.cpp b/base/scopeguard_test.cpp
index e11154a..9236d7b 100644
--- a/base/scopeguard_test.cpp
+++ b/base/scopeguard_test.cpp
@@ -17,6 +17,7 @@
#include "android-base/scopeguard.h"
#include <utility>
+#include <vector>
#include <gtest/gtest.h>
@@ -44,3 +45,15 @@
EXPECT_FALSE(scopeguard.active());
ASSERT_FALSE(guarded_var);
}
+
+TEST(scopeguard, vector) {
+ int guarded_var = 0;
+ {
+ std::vector<android::base::ScopeGuard<std::function<void()>>> scopeguards;
+ scopeguards.emplace_back(android::base::make_scope_guard(
+ std::bind([](int& guarded_var) { guarded_var++; }, std::ref(guarded_var))));
+ scopeguards.emplace_back(android::base::make_scope_guard(
+ std::bind([](int& guarded_var) { guarded_var++; }, std::ref(guarded_var))));
+ }
+ ASSERT_EQ(guarded_var, 2);
+}
diff --git a/bootstat/boot_reason_test.sh b/bootstat/boot_reason_test.sh
index 86e8789..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}
}
@@ -795,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
@@ -1054,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 cfea6ee..e60e6be 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -291,6 +291,17 @@
{"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
@@ -467,6 +478,8 @@
}
return std::string::npos;
}
+
+ operator const std::string&() const { return console; }
};
// If bit error match to needle, correct it.
@@ -503,12 +516,155 @@
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) ||
(console.rfind("PC is at sysrq_handle_crash+") != std::string::npos)) {
- // Can not happen, except on userdebug, during testing/debugging.
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") !=
@@ -520,6 +676,43 @@
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;
}
@@ -527,24 +720,12 @@
return addKernelPanicSubReason(pstoreConsole(content), ret);
}
-// 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 : '?'; });
-}
-
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
@@ -647,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) {
- correctForBitErrorOrUnderline(subReason, s);
- }
- for (const auto& m : kBootReasonMap) {
- if (m.first.length() <= strlen("cold")) continue; // too short?
- if (correctForBitErrorOrUnderline(subReason, m.first + "'")) continue;
- if (m.first.length() <= strlen("reboot,cold")) continue; // short?
- if (!android::base::StartsWith(m.first, "reboot,")) continue;
- correctForBitErrorOrUnderline(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.
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index 271b792..60b7124 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -27,7 +27,6 @@
*/
#include "fastboot.h"
-#include "fs.h"
#include <errno.h>
#include <stdarg.h>
@@ -38,150 +37,117 @@
#include <sys/types.h>
#include <unistd.h>
-#define OP_DOWNLOAD 1
-#define OP_COMMAND 2
-#define OP_QUERY 3
-#define OP_NOTICE 4
-#define OP_DOWNLOAD_SPARSE 5
-#define OP_WAIT_FOR_DISCONNECT 6
-#define OP_DOWNLOAD_FD 7
-#define OP_UPLOAD 8
+#include <memory>
+#include <vector>
-typedef struct Action Action;
+#include <android-base/stringprintf.h>
-#define CMD_SIZE 64
-
-struct Action {
- unsigned op;
- Action* next;
-
- char cmd[CMD_SIZE];
- const char* prod;
- void* data;
- int fd;
-
- // The protocol only supports 32-bit sizes, so you'll have to break
- // anything larger into chunks.
- uint32_t size;
-
- const char *msg;
- int (*func)(Action* a, int status, const char* resp);
-
- double start;
+enum Op {
+ OP_DOWNLOAD,
+ OP_COMMAND,
+ OP_QUERY,
+ OP_NOTICE,
+ OP_DOWNLOAD_SPARSE,
+ OP_WAIT_FOR_DISCONNECT,
+ OP_DOWNLOAD_FD,
+ OP_UPLOAD,
};
-static Action *action_list = 0;
-static Action *action_last = 0;
+struct Action {
+ Action(Op op, const std::string& cmd) : op(op), cmd(cmd) {}
+ Op op;
+ std::string cmd;
+ std::string msg;
+ std::string product;
+ void* data = nullptr;
+ // The protocol only supports 32-bit sizes, so you'll have to break
+ // anything larger into multiple chunks.
+ uint32_t size = 0;
+
+ int fd = -1;
+
+ int (*func)(Action& a, int status, const char* resp) = nullptr;
+
+ double start = -1;
+};
+
+static std::vector<std::unique_ptr<Action>> action_list;
bool fb_getvar(Transport* transport, const std::string& key, std::string* value) {
- std::string cmd = "getvar:";
- cmd += key;
+ std::string cmd = "getvar:" + key;
char buf[FB_RESPONSE_SZ + 1];
memset(buf, 0, sizeof(buf));
- if (fb_command_response(transport, cmd.c_str(), buf)) {
- return false;
+ if (fb_command_response(transport, cmd, buf)) {
+ return false;
}
*value = buf;
return true;
}
-static int cb_default(Action* a, int status, const char* resp) {
+static int cb_default(Action& a, int status, const char* resp) {
if (status) {
fprintf(stderr,"FAILED (%s)\n", resp);
} else {
double split = now();
- fprintf(stderr,"OKAY [%7.3fs]\n", (split - a->start));
- a->start = split;
+ fprintf(stderr, "OKAY [%7.3fs]\n", (split - a.start));
+ a.start = split;
}
return status;
}
-static Action *queue_action(unsigned op, const char *fmt, ...)
-{
- va_list ap;
- size_t cmdsize;
-
- Action* a = reinterpret_cast<Action*>(calloc(1, sizeof(Action)));
- if (a == nullptr) die("out of memory");
-
- va_start(ap, fmt);
- cmdsize = vsnprintf(a->cmd, sizeof(a->cmd), fmt, ap);
- va_end(ap);
-
- if (cmdsize >= sizeof(a->cmd)) {
- free(a);
- die("Command length (%zu) exceeds maximum size (%zu)", cmdsize, sizeof(a->cmd));
- }
-
- if (action_last) {
- action_last->next = a;
- } else {
- action_list = a;
- }
- action_last = a;
- a->op = op;
+static Action& queue_action(Op op, const std::string& cmd) {
+ std::unique_ptr<Action> a{new Action(op, cmd)};
a->func = cb_default;
- a->start = -1;
-
- return a;
+ action_list.push_back(std::move(a));
+ return *action_list.back();
}
-void fb_set_active(const char *slot)
-{
- Action *a;
- a = queue_action(OP_COMMAND, "set_active:%s", slot);
- a->msg = mkmsg("Setting current slot to '%s'", slot);
+void fb_set_active(const std::string& slot) {
+ Action& a = queue_action(OP_COMMAND, "set_active:" + slot);
+ a.msg = "Setting current slot to '" + slot + "'...";
}
-void fb_queue_erase(const char *ptn)
-{
- Action *a;
- a = queue_action(OP_COMMAND, "erase:%s", ptn);
- a->msg = mkmsg("erasing '%s'", ptn);
+void fb_queue_erase(const std::string& partition) {
+ Action& a = queue_action(OP_COMMAND, "erase:" + partition);
+ a.msg = "Erasing '" + partition + "'...";
}
-void fb_queue_flash_fd(const char *ptn, int fd, uint32_t sz)
-{
- Action *a;
+void fb_queue_flash_fd(const std::string& partition, int fd, uint32_t sz) {
+ Action& a = queue_action(OP_DOWNLOAD_FD, "");
+ a.fd = fd;
+ a.size = sz;
+ a.msg = android::base::StringPrintf("Sending '%s' (%d KB)...", partition.c_str(), sz / 1024);
- a = queue_action(OP_DOWNLOAD_FD, "");
- a->fd = fd;
- a->size = sz;
- a->msg = mkmsg("sending '%s' (%d KB)", ptn, sz / 1024);
-
- a = queue_action(OP_COMMAND, "flash:%s", ptn);
- a->msg = mkmsg("writing '%s'", ptn);
+ Action& b = queue_action(OP_COMMAND, "flash:" + partition);
+ b.msg = "Writing '" + partition + "'...";
}
-void fb_queue_flash(const char *ptn, void *data, uint32_t sz)
-{
- Action *a;
+void fb_queue_flash(const std::string& partition, void* data, uint32_t sz) {
+ Action& a = queue_action(OP_DOWNLOAD, "");
+ a.data = data;
+ a.size = sz;
+ a.msg = android::base::StringPrintf("Sending '%s' (%d KB)...", partition.c_str(), sz / 1024);
- a = queue_action(OP_DOWNLOAD, "");
- a->data = data;
- a->size = sz;
- a->msg = mkmsg("sending '%s' (%d KB)", ptn, sz / 1024);
-
- a = queue_action(OP_COMMAND, "flash:%s", ptn);
- a->msg = mkmsg("writing '%s'", ptn);
+ Action& b = queue_action(OP_COMMAND, "flash:" + partition);
+ b.msg = "Writing '" + partition + "'...";
}
-void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, uint32_t sz, size_t current,
- size_t total) {
- Action *a;
+void fb_queue_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
+ size_t current, size_t total) {
+ Action& a = queue_action(OP_DOWNLOAD_SPARSE, "");
+ a.data = s;
+ a.size = 0;
+ a.msg = android::base::StringPrintf("Sending sparse '%s' %zu/%zu (%d KB)...", partition.c_str(),
+ current, total, sz / 1024);
- a = queue_action(OP_DOWNLOAD_SPARSE, "");
- a->data = s;
- a->size = 0;
- a->msg = mkmsg("sending sparse '%s' %zu/%zu (%d KB)", ptn, current, total, sz / 1024);
-
- a = queue_action(OP_COMMAND, "flash:%s", ptn);
- a->msg = mkmsg("writing '%s' %zu/%zu", ptn, current, total);
+ Action& b = queue_action(OP_COMMAND, "flash:" + partition);
+ b.msg =
+ android::base::StringPrintf("Writing '%s' %zu/%zu...", partition.c_str(), current, total);
}
static int match(const char* str, const char** value, unsigned count) {
@@ -205,212 +171,181 @@
return 0;
}
-
-
-static int cb_check(Action* a, int status, const char* resp, int invert)
-{
- const char** value = reinterpret_cast<const char**>(a->data);
- unsigned count = a->size;
+static int cb_check(Action& a, int status, const char* resp, int invert) {
+ const char** value = reinterpret_cast<const char**>(a.data);
+ unsigned count = a.size;
unsigned n;
- int yes;
if (status) {
fprintf(stderr,"FAILED (%s)\n", resp);
return status;
}
- if (a->prod) {
- if (strcmp(a->prod, cur_product) != 0) {
+ if (!a.product.empty()) {
+ if (a.product != cur_product) {
double split = now();
- fprintf(stderr,"IGNORE, product is %s required only for %s [%7.3fs]\n",
- cur_product, a->prod, (split - a->start));
- a->start = split;
+ fprintf(stderr, "IGNORE, product is %s required only for %s [%7.3fs]\n", cur_product,
+ a.product.c_str(), (split - a.start));
+ a.start = split;
return 0;
}
}
- yes = match(resp, value, count);
+ int yes = match(resp, value, count);
if (invert) yes = !yes;
if (yes) {
double split = now();
- fprintf(stderr,"OKAY [%7.3fs]\n", (split - a->start));
- a->start = split;
+ fprintf(stderr, "OKAY [%7.3fs]\n", (split - a.start));
+ a.start = split;
return 0;
}
- fprintf(stderr,"FAILED\n\n");
- fprintf(stderr,"Device %s is '%s'.\n", a->cmd + 7, resp);
- fprintf(stderr,"Update %s '%s'",
- invert ? "rejects" : "requires", value[0]);
+ fprintf(stderr, "FAILED\n\n");
+ fprintf(stderr, "Device %s is '%s'.\n", a.cmd.c_str() + 7, resp);
+ fprintf(stderr, "Update %s '%s'", invert ? "rejects" : "requires", value[0]);
for (n = 1; n < count; n++) {
- fprintf(stderr," or '%s'", value[n]);
+ fprintf(stderr, " or '%s'", value[n]);
}
- fprintf(stderr,".\n\n");
+ fprintf(stderr, ".\n\n");
return -1;
}
-static int cb_require(Action*a, int status, const char* resp) {
+static int cb_require(Action& a, int status, const char* resp) {
return cb_check(a, status, resp, 0);
}
-static int cb_reject(Action* a, int status, const char* resp) {
+static int cb_reject(Action& a, int status, const char* resp) {
return cb_check(a, status, resp, 1);
}
-static char* xstrdup(const char* s) {
- char* result = strdup(s);
- if (!result) die("out of memory");
- return result;
+void fb_queue_require(const std::string& product, const std::string& var, bool invert,
+ size_t nvalues, const char** values) {
+ Action& a = queue_action(OP_QUERY, "getvar:" + var);
+ a.product = product;
+ a.data = values;
+ a.size = nvalues;
+ a.msg = "Checking " + var;
+ a.func = invert ? cb_reject : cb_require;
+ if (a.data == nullptr) die("out of memory");
}
-void fb_queue_require(const char *prod, const char *var,
- bool invert, size_t nvalues, const char **value)
-{
- Action *a;
- a = queue_action(OP_QUERY, "getvar:%s", var);
- a->prod = prod;
- a->data = value;
- a->size = nvalues;
- a->msg = mkmsg("checking %s", var);
- a->func = invert ? cb_reject : cb_require;
- if (a->data == nullptr) die("out of memory");
-}
-
-static int cb_display(Action* a, int status, const char* resp) {
+static int cb_display(Action& a, int status, const char* resp) {
if (status) {
- fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
+ fprintf(stderr, "%s FAILED (%s)\n", a.cmd.c_str(), resp);
return status;
}
- fprintf(stderr, "%s: %s\n", static_cast<const char*>(a->data), resp);
- free(static_cast<char*>(a->data));
+ fprintf(stderr, "%s: %s\n", static_cast<const char*>(a.data), resp);
+ free(static_cast<char*>(a.data));
return 0;
}
-void fb_queue_display(const char* var, const char* prettyname) {
- Action* a = queue_action(OP_QUERY, "getvar:%s", var);
- a->data = xstrdup(prettyname);
- a->func = cb_display;
+void fb_queue_display(const std::string& label, const std::string& var) {
+ Action& a = queue_action(OP_QUERY, "getvar:" + var);
+ a.data = xstrdup(label.c_str());
+ a.func = cb_display;
}
-static int cb_save(Action* a, int status, const char* resp) {
+static int cb_save(Action& a, int status, const char* resp) {
if (status) {
- fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
+ fprintf(stderr, "%s FAILED (%s)\n", a.cmd.c_str(), resp);
return status;
}
- strncpy(reinterpret_cast<char*>(a->data), resp, a->size);
+ strncpy(reinterpret_cast<char*>(a.data), resp, a.size);
return 0;
}
-void fb_queue_query_save(const char* var, char* dest, uint32_t dest_size) {
- Action* a = queue_action(OP_QUERY, "getvar:%s", var);
- a->data = dest;
- a->size = dest_size;
- a->func = cb_save;
+void fb_queue_query_save(const std::string& var, char* dest, uint32_t dest_size) {
+ Action& a = queue_action(OP_QUERY, "getvar:" + var);
+ a.data = dest;
+ a.size = dest_size;
+ a.func = cb_save;
}
-static int cb_do_nothing(Action*, int , const char*) {
- fprintf(stderr,"\n");
+static int cb_do_nothing(Action&, int, const char*) {
+ fprintf(stderr, "\n");
return 0;
}
-void fb_queue_reboot(void)
-{
- Action *a = queue_action(OP_COMMAND, "reboot");
- a->func = cb_do_nothing;
- a->msg = "rebooting";
+void fb_queue_reboot() {
+ Action& a = queue_action(OP_COMMAND, "reboot");
+ a.func = cb_do_nothing;
+ a.msg = "Rebooting...";
}
-void fb_queue_command(const char *cmd, const char *msg)
-{
- Action *a = queue_action(OP_COMMAND, cmd);
- a->msg = msg;
+void fb_queue_command(const std::string& cmd, const std::string& msg) {
+ Action& a = queue_action(OP_COMMAND, cmd);
+ a.msg = msg;
}
-void fb_queue_download(const char *name, void *data, uint32_t size)
-{
- Action *a = queue_action(OP_DOWNLOAD, "");
- a->data = data;
- a->size = size;
- a->msg = mkmsg("downloading '%s'", name);
+void fb_queue_download(const std::string& name, void* data, uint32_t size) {
+ Action& a = queue_action(OP_DOWNLOAD, "");
+ a.data = data;
+ a.size = size;
+ a.msg = "Downloading '" + name + "'";
}
-void fb_queue_download_fd(const char *name, int fd, uint32_t sz)
-{
- Action *a;
- a = queue_action(OP_DOWNLOAD_FD, "");
- a->fd = fd;
- a->size = sz;
- a->msg = mkmsg("sending '%s' (%d KB)", name, sz / 1024);
+void fb_queue_download_fd(const std::string& name, int fd, uint32_t sz) {
+ Action& a = queue_action(OP_DOWNLOAD_FD, "");
+ a.fd = fd;
+ a.size = sz;
+ a.msg = android::base::StringPrintf("Sending '%s' (%d KB)", name.c_str(), sz / 1024);
}
-void fb_queue_upload(const char* outfile) {
- Action* a = queue_action(OP_UPLOAD, "");
- a->data = xstrdup(outfile);
- a->msg = mkmsg("uploading '%s'", outfile);
+void fb_queue_upload(const std::string& outfile) {
+ Action& a = queue_action(OP_UPLOAD, "");
+ a.data = xstrdup(outfile.c_str());
+ a.msg = "Uploading '" + outfile + "'";
}
-void fb_queue_notice(const char* notice) {
- Action *a = queue_action(OP_NOTICE, "");
- a->data = (void*) notice;
+void fb_queue_notice(const std::string& notice) {
+ Action& a = queue_action(OP_NOTICE, "");
+ a.msg = notice;
}
-void fb_queue_wait_for_disconnect(void)
-{
+void fb_queue_wait_for_disconnect() {
queue_action(OP_WAIT_FOR_DISCONNECT, "");
}
-int64_t fb_execute_queue(Transport* transport)
-{
- Action *a;
- char resp[FB_RESPONSE_SZ+1];
+int64_t fb_execute_queue(Transport* transport) {
int64_t status = 0;
-
- a = action_list;
- if (!a)
- return status;
- resp[FB_RESPONSE_SZ] = 0;
-
- double start = -1;
- for (a = action_list; a; a = a->next) {
+ for (auto& a : action_list) {
a->start = now();
- if (start < 0) start = a->start;
- if (a->msg) {
- // fprintf(stderr,"%30s... ",a->msg);
- fprintf(stderr,"%s...\n",a->msg);
+ if (!a->msg.empty()) {
+ fprintf(stderr, "%s\n", a->msg.c_str());
}
if (a->op == OP_DOWNLOAD) {
status = fb_download_data(transport, a->data, a->size);
- status = a->func(a, status, status ? fb_get_error().c_str() : "");
+ status = a->func(*a, status, status ? fb_get_error().c_str() : "");
if (status) break;
} else if (a->op == OP_DOWNLOAD_FD) {
status = fb_download_data_fd(transport, a->fd, a->size);
- status = a->func(a, status, status ? fb_get_error().c_str() : "");
+ status = a->func(*a, status, status ? fb_get_error().c_str() : "");
if (status) break;
} else if (a->op == OP_COMMAND) {
status = fb_command(transport, a->cmd);
- status = a->func(a, status, status ? fb_get_error().c_str() : "");
+ status = a->func(*a, status, status ? fb_get_error().c_str() : "");
if (status) break;
} else if (a->op == OP_QUERY) {
+ char resp[FB_RESPONSE_SZ + 1] = {};
status = fb_command_response(transport, a->cmd, resp);
- status = a->func(a, status, status ? fb_get_error().c_str() : resp);
+ status = a->func(*a, status, status ? fb_get_error().c_str() : resp);
if (status) break;
} else if (a->op == OP_NOTICE) {
- fprintf(stderr,"%s\n",(char*)a->data);
+ // We already showed the notice because it's in `Action::msg`.
} else if (a->op == OP_DOWNLOAD_SPARSE) {
status = fb_download_data_sparse(transport, reinterpret_cast<sparse_file*>(a->data));
- status = a->func(a, status, status ? fb_get_error().c_str() : "");
+ status = a->func(*a, status, status ? fb_get_error().c_str() : "");
if (status) break;
} else if (a->op == OP_WAIT_FOR_DISCONNECT) {
transport->WaitForDisconnect();
} else if (a->op == OP_UPLOAD) {
status = fb_upload_data(transport, reinterpret_cast<char*>(a->data));
- status = a->func(a, status, status ? fb_get_error().c_str() : "");
+ status = a->func(*a, status, status ? fb_get_error().c_str() : "");
} else {
- die("bogus action");
+ die("unknown action: %d", a->op);
}
}
-
- fprintf(stderr,"finished. total time: %.3fs\n", (now() - start));
+ action_list.clear();
return status;
}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 536d64e..237f081 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -123,6 +123,8 @@
{ nullptr, "boot_other.img", "boot.sig", "boot", true, true },
{ "dtbo", "dtbo.img", "dtbo.sig", "dtbo", true, false },
{ "dts", "dt.img", "dt.sig", "dts", true, false },
+ { "odm", "odm.img", "odm.sig", "odm", true, false },
+ { "product", "product.img", "product.sig", "product", true, false },
{ "recovery", "recovery.img", "recovery.sig", "recovery", true, false },
{ "system", "system.img", "system.sig", "system", false, false },
{ nullptr, "system_other.img", "system.sig", "system", true, true },
@@ -633,27 +635,31 @@
return fd.release();
}
-static char *strip(char *s)
-{
- int n;
- while(*s && isspace(*s)) s++;
- n = strlen(s);
- while(n-- > 0) {
- if(!isspace(s[n])) break;
+static char* strip(char* s) {
+ while (*s && isspace(*s)) s++;
+
+ int n = strlen(s);
+ while (n-- > 0) {
+ if (!isspace(s[n])) break;
s[n] = 0;
}
return s;
}
#define MAX_OPTIONS 32
-static int setup_requirement_line(char *name)
-{
+static void check_requirement(Transport* transport, char* line) {
char *val[MAX_OPTIONS];
- char *prod = nullptr;
- unsigned n, count;
+ unsigned count;
char *x;
int invert = 0;
+ // "require product=alpha|beta|gamma"
+ // "require version-bootloader=1234"
+ // "require-for-product:gamma version-bootloader=istanbul|constantinople"
+ // "require partition-exists=vendor"
+
+ char* name = line;
+ const char* product = "";
if (!strncmp(name, "reject ", 7)) {
name += 7;
invert = 1;
@@ -662,19 +668,46 @@
invert = 0;
} else if (!strncmp(name, "require-for-product:", 20)) {
// Get the product and point name past it
- prod = name + 20;
+ product = name + 20;
name = strchr(name, ' ');
- if (!name) return -1;
+ if (!name) die("android-info.txt syntax error: %s", line);
*name = 0;
name += 1;
invert = 0;
}
x = strchr(name, '=');
- if (x == 0) return 0;
+ if (x == 0) return;
*x = 0;
val[0] = x + 1;
+ name = strip(name);
+
+ // "require partition-exists=x" is a special case, added because of the trouble we had when
+ // Pixel 2 shipped with new partitions and users used old versions of fastboot to flash them,
+ // missing out new partitions. A device with new partitions can use "partition-exists" to
+ // override the `is_optional` field in the `images` array.
+ if (!strcmp(name, "partition-exists")) {
+ const char* partition_name = val[0];
+ std::string has_slot;
+ if (!fb_getvar(transport, std::string("has-slot:") + partition_name, &has_slot) ||
+ (has_slot != "yes" && has_slot != "no")) {
+ die("device doesn't have required partition %s!", partition_name);
+ }
+ bool known_partition = false;
+ for (size_t i = 0; i < arraysize(images); ++i) {
+ if (images[i].nickname && !strcmp(images[i].nickname, partition_name)) {
+ images[i].is_optional = false;
+ known_partition = true;
+ }
+ }
+ if (!known_partition) {
+ die("device requires partition %s which is not known to this version of fastboot",
+ partition_name);
+ }
+ return;
+ }
+
for(count = 1; count < MAX_OPTIONS; count++) {
x = strchr(val[count - 1],'|');
if (x == 0) break;
@@ -682,54 +715,39 @@
val[count] = x + 1;
}
- name = strip(name);
- for(n = 0; n < count; n++) val[n] = strip(val[n]);
-
- name = strip(name);
- if (name == 0) return -1;
-
- const char* var = name;
// Work around an unfortunate name mismatch.
- if (!strcmp(name,"board")) var = "product";
+ const char* var = name;
+ if (!strcmp(name, "board")) var = "product";
const char** out = reinterpret_cast<const char**>(malloc(sizeof(char*) * count));
- if (out == 0) return -1;
+ if (out == nullptr) die("out of memory");
- for(n = 0; n < count; n++) {
- out[n] = strdup(strip(val[n]));
- if (out[n] == 0) {
- for(size_t i = 0; i < n; ++i) {
- free((char*) out[i]);
- }
- free(out);
- return -1;
- }
+ for (size_t i = 0; i < count; ++i) {
+ out[i] = xstrdup(strip(val[i]));
}
- fb_queue_require(prod, var, invert, n, out);
- return 0;
+ fb_queue_require(product, var, invert, count, out);
}
-static void setup_requirements(char* data, int64_t sz) {
+static void check_requirements(Transport* transport, char* data, int64_t sz) {
char* s = data;
while (sz-- > 0) {
if (*s == '\n') {
*s++ = 0;
- if (setup_requirement_line(data)) {
- die("out of memory");
- }
+ check_requirement(transport, data);
data = s;
} else {
s++;
}
}
+ if (fb_execute_queue(transport)) die("requirements not met!");
}
static void queue_info_dump() {
fb_queue_notice("--------------------------------------------");
- fb_queue_display("version-bootloader", "Bootloader Version...");
- fb_queue_display("version-baseband", "Baseband Version.....");
- fb_queue_display("serialno", "Serial Number........");
+ fb_queue_display("Bootloader Version...", "version-bootloader");
+ fb_queue_display("Baseband Version.....", "version-baseband");
+ fb_queue_display("Serial Number........", "serialno");
fb_queue_notice("--------------------------------------------");
}
@@ -889,14 +907,13 @@
lseek(fd, 0, SEEK_SET);
}
-static void flash_buf(const char *pname, struct fastboot_buffer *buf)
+static void flash_buf(const std::string& partition, struct fastboot_buffer *buf)
{
sparse_file** s;
// Rewrite vbmeta if that's what we're flashing and modification has been requested.
if ((g_disable_verity || g_disable_verification) &&
- (strcmp(pname, "vbmeta") == 0 || strcmp(pname, "vbmeta_a") == 0 ||
- strcmp(pname, "vbmeta_b") == 0)) {
+ (partition == "vbmeta" || partition == "vbmeta_a" || partition == "vbmeta_b")) {
rewrite_vbmeta_buffer(buf);
}
@@ -912,12 +929,12 @@
for (size_t i = 0; i < sparse_files.size(); ++i) {
const auto& pair = sparse_files[i];
- fb_queue_flash_sparse(pname, pair.first, pair.second, i + 1, sparse_files.size());
+ fb_queue_flash_sparse(partition, pair.first, pair.second, i + 1, sparse_files.size());
}
break;
}
case FB_BUFFER_FD:
- fb_queue_flash_fd(pname, buf->fd, buf->sz);
+ fb_queue_flash_fd(partition, buf->fd, buf->sz);
break;
default:
die("unknown buffer type: %d", buf->type);
@@ -1115,11 +1132,11 @@
}
}
if (slot_override != "") {
- fb_set_active((separator + slot_override).c_str());
+ fb_set_active(separator + slot_override);
} else {
std::string current_slot = get_current_slot(transport);
if (current_slot != "") {
- fb_set_active((separator + current_slot).c_str());
+ fb_set_active(separator + current_slot);
}
}
}
@@ -1141,7 +1158,7 @@
die("update package '%s' has no android-info.txt", filename);
}
- setup_requirements(reinterpret_cast<char*>(data), sz);
+ check_requirements(transport, reinterpret_cast<char*>(data), sz);
std::string secondary;
if (!skip_secondary) {
@@ -1183,7 +1200,7 @@
auto update = [&](const std::string& partition) {
do_update_signature(zip, images[i].sig_name);
if (erase_first && needs_erase(transport, partition.c_str())) {
- fb_queue_erase(partition.c_str());
+ fb_queue_erase(partition);
}
flash_buf(partition.c_str(), &buf);
/* not closing the fd here since the sparse code keeps the fd around
@@ -1230,7 +1247,7 @@
void* data = load_file(fname.c_str(), &sz);
if (data == nullptr) die("could not load android-info.txt: %s", strerror(errno));
- setup_requirements(reinterpret_cast<char*>(data), sz);
+ check_requirements(transport, reinterpret_cast<char*>(data), sz);
std::string secondary;
if (!skip_secondary) {
@@ -1265,7 +1282,7 @@
auto flashall = [&](const std::string &partition) {
do_send_signature(fname.c_str());
if (erase_first && needs_erase(transport, partition.c_str())) {
- fb_queue_erase(partition.c_str());
+ fb_queue_erase(partition);
}
flash_buf(partition.c_str(), &buf);
};
@@ -1305,7 +1322,7 @@
while (!args->empty()) {
command += " " + next_arg(args);
}
- fb_queue_command(command.c_str(), "");
+ fb_queue_command(command, "");
}
static int64_t parse_num(const char *arg)
@@ -1360,8 +1377,8 @@
static unsigned fb_get_flash_block_size(Transport* transport, std::string name) {
std::string sizeString;
- if (!fb_getvar(transport, name.c_str(), &sizeString) || sizeString.empty()) {
- /* This device does not report flash block sizes, so return 0 */
+ if (!fb_getvar(transport, name, &sizeString) || sizeString.empty()) {
+ // This device does not report flash block sizes, so return 0.
return 0;
}
sizeString = fb_fix_numeric_var(sizeString);
@@ -1379,7 +1396,7 @@
}
static void fb_perform_format(Transport* transport,
- const char* partition, int skip_if_not_supported,
+ const std::string& partition, int skip_if_not_supported,
const std::string& type_override, const std::string& size_override,
const std::string& initial_dir) {
std::string partition_type, partition_size;
@@ -1398,26 +1415,26 @@
limit = sparse_limit;
}
- if (!fb_getvar(transport, std::string("partition-type:") + partition, &partition_type)) {
+ if (!fb_getvar(transport, "partition-type:" + partition, &partition_type)) {
errMsg = "Can't determine partition type.\n";
goto failed;
}
if (!type_override.empty()) {
if (partition_type != type_override) {
fprintf(stderr, "Warning: %s type is %s, but %s was requested for formatting.\n",
- partition, partition_type.c_str(), type_override.c_str());
+ partition.c_str(), partition_type.c_str(), type_override.c_str());
}
partition_type = type_override;
}
- if (!fb_getvar(transport, std::string("partition-size:") + partition, &partition_size)) {
+ if (!fb_getvar(transport, "partition-size:" + partition, &partition_size)) {
errMsg = "Unable to get partition size\n";
goto failed;
}
if (!size_override.empty()) {
if (partition_size != size_override) {
fprintf(stderr, "Warning: %s size is %s, but %s was requested for formatting.\n",
- partition, partition_size.c_str(), size_override.c_str());
+ partition.c_str(), partition_size.c_str(), size_override.c_str());
}
partition_size = size_override;
}
@@ -1447,7 +1464,7 @@
if (fs_generator_generate(gen, output.path, size, initial_dir,
eraseBlkSize, logicalBlkSize)) {
- die("Cannot generate image for %s", partition);
+ die("Cannot generate image for %s", partition.c_str());
return;
}
@@ -1630,6 +1647,8 @@
return 1;
}
+ const double start = now();
+
if (!supports_AB(transport) && supports_AB_obsolete(transport)) {
fprintf(stderr, "Warning: Device A/B support is outdated. Bootloader update required.\n");
}
@@ -1657,7 +1676,7 @@
if (command == "getvar") {
std::string variable = next_arg(&args);
- fb_queue_display(variable.c_str(), variable.c_str());
+ fb_queue_display(variable, variable);
} else if (command == "erase") {
std::string partition = next_arg(&args);
auto erase = [&](const std::string& partition) {
@@ -1669,7 +1688,7 @@
partition_type.c_str());
}
- fb_queue_erase(partition.c_str());
+ fb_queue_erase(partition);
};
do_for_partitions(transport, partition, slot_override, erase, true);
} else if (android::base::StartsWith(command, "format")) {
@@ -1690,10 +1709,9 @@
auto format = [&](const std::string& partition) {
if (erase_first && needs_erase(transport, partition.c_str())) {
- fb_queue_erase(partition.c_str());
+ fb_queue_erase(partition);
}
- fb_perform_format(transport, partition.c_str(), 0, type_override, size_override,
- "");
+ fb_perform_format(transport, partition, 0, type_override, size_override, "");
};
do_for_partitions(transport, partition.c_str(), slot_override, format, true);
} else if (command == "signature") {
@@ -1747,7 +1765,7 @@
auto flash = [&](const std::string &partition) {
if (erase_first && needs_erase(transport, partition.c_str())) {
- fb_queue_erase(partition.c_str());
+ fb_queue_erase(partition);
}
do_flash(transport, partition.c_str(), fname.c_str());
};
@@ -1762,7 +1780,7 @@
data = load_bootable_image(kernel, ramdisk, second_stage, &sz, cmdline);
auto flashraw = [&](const std::string& partition) {
- fb_queue_flash(partition.c_str(), data, sz);
+ fb_queue_flash(partition, data, sz);
};
do_for_partitions(transport, partition, slot_override, flashraw, true);
} else if (command == "flashall") {
@@ -1796,7 +1814,7 @@
fb_getvar(transport, "slot-suffixes", &var)) {
slot = "_" + slot;
}
- fb_set_active(slot.c_str());
+ fb_set_active(slot);
} else if (command == "stage") {
std::string filename = next_arg(&args);
@@ -1804,10 +1822,10 @@
if (!load_buf(transport, filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
die("cannot load '%s'", filename.c_str());
}
- fb_queue_download_fd(filename.c_str(), buf.fd, buf.sz);
+ fb_queue_download_fd(filename, buf.fd, buf.sz);
} else if (command == "get_staged") {
std::string filename = next_arg(&args);
- fb_queue_upload(filename.c_str());
+ fb_queue_upload(filename);
} else if (command == "oem") {
do_oem_command("oem", &args);
} else if (command == "flashing") {
@@ -1853,7 +1871,7 @@
}
}
if (wants_set_active) {
- fb_set_active(next_active.c_str());
+ fb_set_active(next_active);
}
if (wants_reboot && !skip_reboot) {
fb_queue_reboot();
@@ -1866,5 +1884,7 @@
fb_queue_wait_for_disconnect();
}
- return fb_execute_queue(transport) ? EXIT_FAILURE : EXIT_SUCCESS;
+ int status = fb_execute_queue(transport) ? EXIT_FAILURE : EXIT_SUCCESS;
+ fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start));
+ return status;
}
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index f4faa21..a31057a 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -39,8 +39,8 @@
struct sparse_file;
/* protocol.c - fastboot protocol */
-int fb_command(Transport* transport, const char* cmd);
-int fb_command_response(Transport* transport, const char* cmd, char* response);
+int fb_command(Transport* transport, const std::string& cmd);
+int fb_command_response(Transport* transport, const std::string& cmd, char* response);
int64_t fb_download_data(Transport* transport, const void* data, uint32_t size);
int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size);
int fb_download_data_sparse(Transport* transport, struct sparse_file* s);
@@ -52,29 +52,29 @@
/* engine.c - high level command queue engine */
bool fb_getvar(Transport* transport, const std::string& key, std::string* value);
-void fb_queue_flash(const char *ptn, void *data, uint32_t sz);
-void fb_queue_flash_fd(const char *ptn, int fd, uint32_t sz);
-void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, uint32_t sz, size_t current,
- size_t total);
-void fb_queue_erase(const char *ptn);
-void fb_queue_format(const char *ptn, int skip_if_not_supported, int32_t max_chunk_sz);
-void fb_queue_require(const char *prod, const char *var, bool invert,
- size_t nvalues, const char **value);
-void fb_queue_display(const char *var, const char *prettyname);
-void fb_queue_query_save(const char *var, char *dest, uint32_t dest_size);
+void fb_queue_flash(const std::string& partition, void* data, uint32_t sz);
+void fb_queue_flash_fd(const std::string& partition, int fd, uint32_t sz);
+void fb_queue_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
+ size_t current, size_t total);
+void fb_queue_erase(const std::string& partition);
+void fb_queue_format(const std::string& partition, int skip_if_not_supported, int32_t max_chunk_sz);
+void fb_queue_require(const std::string& prod, const std::string& var, bool invert, size_t nvalues,
+ const char** values);
+void fb_queue_display(const std::string& label, const std::string& var);
+void fb_queue_query_save(const std::string& var, char* dest, uint32_t dest_size);
void fb_queue_reboot(void);
-void fb_queue_command(const char *cmd, const char *msg);
-void fb_queue_download(const char *name, void *data, uint32_t size);
-void fb_queue_download_fd(const char *name, int fd, uint32_t sz);
-void fb_queue_upload(const char* outfile);
-void fb_queue_notice(const char *notice);
+void fb_queue_command(const std::string& cmd, const std::string& msg);
+void fb_queue_download(const std::string& name, void* data, uint32_t size);
+void fb_queue_download_fd(const std::string& name, int fd, uint32_t sz);
+void fb_queue_upload(const std::string& outfile);
+void fb_queue_notice(const std::string& notice);
void fb_queue_wait_for_disconnect(void);
int64_t fb_execute_queue(Transport* transport);
-void fb_set_active(const char *slot);
+void fb_set_active(const std::string& slot);
/* util stuff */
double now();
-char *mkmsg(const char *fmt, ...);
+char* xstrdup(const char*);
// These printf-like functions are implemented in terms of vsnprintf, so they
// use the same attribute for compile-time format string checking. On Windows,
diff --git a/fastboot/protocol.cpp b/fastboot/protocol.cpp
index dcdf8f0..c239861 100644
--- a/fastboot/protocol.cpp
+++ b/fastboot/protocol.cpp
@@ -113,10 +113,10 @@
return -1;
}
-static int64_t _command_start(Transport* transport, const char* cmd, uint32_t size, char* response) {
- size_t cmdsize = strlen(cmd);
- if (cmdsize > 64) {
- g_error = android::base::StringPrintf("command too large (%zu)", cmdsize);
+static int64_t _command_start(Transport* transport, const std::string& cmd, uint32_t size,
+ char* response) {
+ if (cmd.size() > 64) {
+ g_error = android::base::StringPrintf("command too large (%zu)", cmd.size());
return -1;
}
@@ -124,7 +124,7 @@
response[0] = 0;
}
- if (transport->Write(cmd, cmdsize) != static_cast<int>(cmdsize)) {
+ if (transport->Write(cmd.c_str(), cmd.size()) != static_cast<int>(cmd.size())) {
g_error = android::base::StringPrintf("command write failed (%s)", strerror(errno));
transport->Close();
return -1;
@@ -167,8 +167,8 @@
return check_response(transport, 0, 0) < 0 ? -1 : 0;
}
-static int64_t _command_send(Transport* transport, const char* cmd, const void* data, uint32_t size,
- char* response) {
+static int64_t _command_send(Transport* transport, const std::string& cmd, const void* data,
+ uint32_t size, char* response) {
if (size == 0) {
return -1;
}
@@ -190,7 +190,7 @@
return size;
}
-static int64_t _command_send_fd(Transport* transport, const char* cmd, int fd, uint32_t size,
+static int64_t _command_send_fd(Transport* transport, const std::string& cmd, int fd, uint32_t size,
char* response) {
static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
off64_t offset = 0;
@@ -223,15 +223,15 @@
return size;
}
-static int _command_send_no_data(Transport* transport, const char* cmd, char* response) {
+static int _command_send_no_data(Transport* transport, const std::string& cmd, char* response) {
return _command_start(transport, cmd, 0, response);
}
-int fb_command(Transport* transport, const char* cmd) {
+int fb_command(Transport* transport, const std::string& cmd) {
return _command_send_no_data(transport, cmd, 0);
}
-int fb_command_response(Transport* transport, const char* cmd, char* response) {
+int fb_command_response(Transport* transport, const std::string& cmd, char* response) {
return _command_send_no_data(transport, cmd, response);
}
@@ -339,7 +339,7 @@
}
std::string cmd(android::base::StringPrintf("download:%08x", size));
- int r = _command_start(transport, cmd.c_str(), size, 0);
+ int r = _command_start(transport, cmd, size, 0);
if (r < 0) {
return -1;
}
diff --git a/fastboot/util.cpp b/fastboot/util.cpp
index f2bbd34..fb085e7 100644
--- a/fastboot/util.cpp
+++ b/fastboot/util.cpp
@@ -35,35 +35,24 @@
#include "fastboot.h"
-double now()
-{
+double now() {
struct timeval tv;
gettimeofday(&tv, NULL);
return (double)tv.tv_sec + (double)tv.tv_usec / 1000000;
}
-char *mkmsg(const char *fmt, ...)
-{
- char buf[256];
- char *s;
- va_list ap;
-
- va_start(ap, fmt);
- vsprintf(buf, fmt, ap);
- va_end(ap);
-
- s = strdup(buf);
- if (s == 0) die("out of memory");
- return s;
-}
-
-void die(const char *fmt, ...)
-{
+void die(const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
fprintf(stderr,"error: ");
vfprintf(stderr, fmt, ap);
fprintf(stderr,"\n");
va_end(ap);
- exit(1);
+ exit(EXIT_FAILURE);
+}
+
+char* xstrdup(const char* s) {
+ char* result = strdup(s);
+ if (!result) die("out of memory");
+ return result;
}
diff --git a/init/stable_properties.h b/init/stable_properties.h
index bd568f0..c8bdaa4 100644
--- a/init/stable_properties.h
+++ b/init/stable_properties.h
@@ -37,6 +37,7 @@
"persist.sys.zram_enabled",
"ro.bootmode",
"ro.build.type",
+ "ro.debuggable",
"sys.boot_completed",
"sys.retaildemo.enabled",
"sys.shutdown.requested",
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/libcutils/include/cutils/trace.h b/libcutils/include/cutils/trace.h
index b2779b2..bbb150d 100644
--- a/libcutils/include/cutils/trace.h
+++ b/libcutils/include/cutils/trace.h
@@ -73,7 +73,8 @@
#define ATRACE_TAG_NETWORK (1<<21)
#define ATRACE_TAG_ADB (1<<22)
#define ATRACE_TAG_VIBRATOR (1<<23)
-#define ATRACE_TAG_LAST ATRACE_TAG_VIBRATOR
+#define ATRACE_TAG_AIDL (1<<24)
+#define ATRACE_TAG_LAST ATRACE_TAG_AIDL
// Reserved for initialization.
#define ATRACE_TAG_NOT_READY (1ULL<<63)
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 08dcf77..0d43f87 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -194,6 +194,7 @@
"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 aa8cd3a..6ecedce 100644
--- a/libunwindstack/DwarfCfa.cpp
+++ b/libunwindstack/DwarfCfa.cpp
@@ -50,7 +50,17 @@
memory_->set_cur_offset(start_offset);
uint64_t cfa_offset;
cur_pc_ = fde_->pc_start;
- while ((cfa_offset = memory_->cur_offset()) < end_offset && cur_pc_ <= pc) {
+ loc_regs->pc_start = cur_pc_;
+ while (true) {
+ if (cur_pc_ > pc) {
+ loc_regs->pc_end = cur_pc_;
+ return true;
+ }
+ if ((cfa_offset = memory_->cur_offset()) >= end_offset) {
+ loc_regs->pc_end = fde_->pc_end;
+ return true;
+ }
+ loc_regs->pc_start = cur_pc_;
operands_.clear();
// Read the cfa information.
uint8_t cfa_value;
@@ -129,7 +139,6 @@
}
}
}
- return true;
}
template <typename AddressType>
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 5586e72..65eec65 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -55,21 +55,29 @@
}
bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
- last_error_.code = DWARF_ERROR_NONE;
- const DwarfFde* fde = GetFdeFromPc(pc);
- if (fde == nullptr || fde->cie == nullptr) {
- last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
- return false;
- }
+ // Lookup the pc in the cache.
+ auto it = loc_regs_.upper_bound(pc);
+ if (it == loc_regs_.end() || pc < it->second.pc_start) {
+ last_error_.code = DWARF_ERROR_NONE;
+ const DwarfFde* fde = GetFdeFromPc(pc);
+ if (fde == nullptr || fde->cie == nullptr) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
- // Now get the location information for this pc.
- dwarf_loc_regs_t loc_regs;
- if (!GetCfaLocationInfo(pc, fde, &loc_regs)) {
- return false;
+ // Now get the location information for this pc.
+ dwarf_loc_regs_t loc_regs;
+ if (!GetCfaLocationInfo(pc, fde, &loc_regs)) {
+ return false;
+ }
+ loc_regs.cie = fde->cie;
+
+ // Store it in the cache.
+ it = loc_regs_.emplace(loc_regs.pc_end, std::move(loc_regs)).first;
}
// Now eval the actual registers.
- return Eval(fde->cie, process_memory, loc_regs, regs, finished);
+ return Eval(it->second.cie, process_memory, it->second, regs, finished);
}
template <typename AddressType>
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 e2a9cb0..27cab43 100644
--- a/libunwindstack/RegsArm.cpp
+++ b/libunwindstack/RegsArm.cpp
@@ -51,13 +51,23 @@
}
uint64_t RegsArm::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
+ if (!elf->valid()) {
+ return 2;
+ }
+
uint64_t load_bias = elf->GetLoadBias();
if (rel_pc < load_bias) {
- return 0;
+ if (rel_pc < 2) {
+ return 0;
+ }
+ return 2;
}
uint64_t adjusted_rel_pc = rel_pc - load_bias;
if (adjusted_rel_pc < 5) {
- return 0;
+ if (adjusted_rel_pc < 2) {
+ return 0;
+ }
+ return 2;
}
if (adjusted_rel_pc & 1) {
diff --git a/libunwindstack/RegsArm64.cpp b/libunwindstack/RegsArm64.cpp
index fe24c80..4a2a6c4 100644
--- a/libunwindstack/RegsArm64.cpp
+++ b/libunwindstack/RegsArm64.cpp
@@ -51,8 +51,8 @@
regs_[ARM64_REG_SP] = sp;
}
-uint64_t RegsArm64::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
- if (!elf->valid() || rel_pc < 4) {
+uint64_t RegsArm64::GetPcAdjustment(uint64_t rel_pc, Elf*) {
+ if (rel_pc < 4) {
return 0;
}
return 4;
diff --git a/libunwindstack/RegsMips.cpp b/libunwindstack/RegsMips.cpp
index 0b10e21..c87e69b 100644
--- a/libunwindstack/RegsMips.cpp
+++ b/libunwindstack/RegsMips.cpp
@@ -51,8 +51,8 @@
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) {
+uint64_t RegsMips::GetPcAdjustment(uint64_t rel_pc, Elf*) {
+ if (rel_pc < 8) {
return 0;
}
// For now, just assume no compact branches
diff --git a/libunwindstack/RegsMips64.cpp b/libunwindstack/RegsMips64.cpp
index 8848e3b..236a922 100644
--- a/libunwindstack/RegsMips64.cpp
+++ b/libunwindstack/RegsMips64.cpp
@@ -51,8 +51,8 @@
regs_[MIPS64_REG_SP] = sp;
}
-uint64_t RegsMips64::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
- if (!elf->valid() || rel_pc < 8) {
+uint64_t RegsMips64::GetPcAdjustment(uint64_t rel_pc, Elf*) {
+ if (rel_pc < 8) {
return 0;
}
// For now, just assume no compact branches
diff --git a/libunwindstack/RegsX86.cpp b/libunwindstack/RegsX86.cpp
index bb95a13..f7e0614 100644
--- a/libunwindstack/RegsX86.cpp
+++ b/libunwindstack/RegsX86.cpp
@@ -50,8 +50,8 @@
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) {
+uint64_t RegsX86::GetPcAdjustment(uint64_t rel_pc, Elf*) {
+ if (rel_pc == 0) {
return 0;
}
return 1;
diff --git a/libunwindstack/RegsX86_64.cpp b/libunwindstack/RegsX86_64.cpp
index e57e2bc..7d6ad86 100644
--- a/libunwindstack/RegsX86_64.cpp
+++ b/libunwindstack/RegsX86_64.cpp
@@ -50,8 +50,8 @@
regs_[X86_64_REG_SP] = sp;
}
-uint64_t RegsX86_64::GetPcAdjustment(uint64_t rel_pc, Elf* elf) {
- if (!elf->valid() || rel_pc == 0) {
+uint64_t RegsX86_64::GetPcAdjustment(uint64_t rel_pc, Elf*) {
+ if (rel_pc == 0) {
return 0;
}
return 1;
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 27262bd..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)
@@ -142,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/DwarfLocation.h b/libunwindstack/include/unwindstack/DwarfLocation.h
index 0881182..3d50ccf 100644
--- a/libunwindstack/include/unwindstack/DwarfLocation.h
+++ b/libunwindstack/include/unwindstack/DwarfLocation.h
@@ -23,6 +23,8 @@
namespace unwindstack {
+struct DwarfCie;
+
enum DwarfLocationEnum : uint8_t {
DWARF_LOCATION_INVALID = 0,
DWARF_LOCATION_UNDEFINED,
@@ -38,7 +40,13 @@
uint64_t values[2];
};
-typedef std::unordered_map<uint32_t, DwarfLocation> dwarf_loc_regs_t;
+struct DwarfLocations : public std::unordered_map<uint32_t, DwarfLocation> {
+ const DwarfCie* cie;
+ // The range of PCs where the locations are valid (end is exclusive).
+ uint64_t pc_start = 0;
+ uint64_t pc_end = 0;
+};
+typedef DwarfLocations dwarf_loc_regs_t;
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index da91fd0..209c54a 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <iterator>
+#include <map>
#include <unordered_map>
#include <unwindstack/DwarfError.h>
@@ -112,6 +113,7 @@
std::unordered_map<uint64_t, DwarfFde> fde_entries_;
std::unordered_map<uint64_t, DwarfCie> cie_entries_;
std::unordered_map<uint64_t, dwarf_loc_regs_t> cie_loc_regs_;
+ std::map<uint64_t, dwarf_loc_regs_t> loc_regs_; // Single row indexed by pc_end.
};
template <typename AddressType>
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/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index c85764c..c340291 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -853,7 +853,8 @@
fde.cfa_instructions_offset = 0x6000;
fde.cfa_instructions_end = 0x6002;
- dwarf_loc_regs_t cie_loc_regs{{6, {DWARF_LOCATION_REGISTER, {4, 0}}}};
+ dwarf_loc_regs_t cie_loc_regs;
+ cie_loc_regs[6] = DwarfLocation{DWARF_LOCATION_REGISTER, {4, 0}};
this->section_->TestSetCachedCieLocRegs(0x8000, cie_loc_regs);
this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp
index 3fcd2b6..071d2df 100644
--- a/libunwindstack/tests/DwarfSectionTest.cpp
+++ b/libunwindstack/tests/DwarfSectionTest.cpp
@@ -165,4 +165,72 @@
ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
}
+static bool MockGetCfaLocationInfo(::testing::Unused, const DwarfFde* fde,
+ dwarf_loc_regs_t* loc_regs) {
+ loc_regs->pc_start = fde->pc_start;
+ loc_regs->pc_end = fde->pc_end;
+ return true;
+}
+
+TEST_F(DwarfSectionTest, Step_cache) {
+ MockDwarfSection mock_section(&memory_);
+
+ DwarfCie cie{};
+ DwarfFde fde{};
+ fde.pc_start = 0x500;
+ fde.pc_end = 0x2000;
+ fde.cie = &cie;
+
+ EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+ .WillOnce(::testing::Return(true));
+ EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
+
+ EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
+ .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
+
+ MemoryFake process;
+ EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+ .WillRepeatedly(::testing::Return(true));
+
+ bool finished;
+ ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
+ ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
+ ASSERT_TRUE(mock_section.Step(0x1500, nullptr, &process, &finished));
+}
+
+TEST_F(DwarfSectionTest, Step_cache_not_in_pc) {
+ MockDwarfSection mock_section(&memory_);
+
+ DwarfCie cie{};
+ DwarfFde fde0{};
+ fde0.pc_start = 0x1000;
+ fde0.pc_end = 0x2000;
+ fde0.cie = &cie;
+ EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
+ .WillOnce(::testing::Return(true));
+ EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde0));
+ EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde0, ::testing::_))
+ .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
+
+ MemoryFake process;
+ EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
+ .WillRepeatedly(::testing::Return(true));
+
+ bool finished;
+ ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
+
+ DwarfFde fde1{};
+ fde1.pc_start = 0x500;
+ fde1.pc_end = 0x800;
+ fde1.cie = &cie;
+ EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x600, ::testing::_))
+ .WillOnce(::testing::Return(true));
+ EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde1));
+ EXPECT_CALL(mock_section, GetCfaLocationInfo(0x600, &fde1, ::testing::_))
+ .WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
+
+ ASSERT_TRUE(mock_section.Step(0x600, nullptr, &process, &finished));
+ ASSERT_TRUE(mock_section.Step(0x700, nullptr, &process, &finished));
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 3e80733..d15823e 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -94,48 +94,48 @@
TEST_F(RegsTest, rel_pc) {
RegsArm64 arm64;
- ASSERT_EQ(4U, arm64.GetPcAdjustment(0x10, elf_.get()));
- ASSERT_EQ(4U, arm64.GetPcAdjustment(0x4, elf_.get()));
- ASSERT_EQ(0U, arm64.GetPcAdjustment(0x3, elf_.get()));
- ASSERT_EQ(0U, arm64.GetPcAdjustment(0x2, elf_.get()));
- ASSERT_EQ(0U, arm64.GetPcAdjustment(0x1, elf_.get()));
- ASSERT_EQ(0U, arm64.GetPcAdjustment(0x0, elf_.get()));
+ EXPECT_EQ(4U, arm64.GetPcAdjustment(0x10, elf_.get()));
+ EXPECT_EQ(4U, arm64.GetPcAdjustment(0x4, elf_.get()));
+ EXPECT_EQ(0U, arm64.GetPcAdjustment(0x3, elf_.get()));
+ EXPECT_EQ(0U, arm64.GetPcAdjustment(0x2, elf_.get()));
+ EXPECT_EQ(0U, arm64.GetPcAdjustment(0x1, elf_.get()));
+ EXPECT_EQ(0U, arm64.GetPcAdjustment(0x0, elf_.get()));
RegsX86 x86;
- ASSERT_EQ(1U, x86.GetPcAdjustment(0x100, elf_.get()));
- ASSERT_EQ(1U, x86.GetPcAdjustment(0x2, elf_.get()));
- ASSERT_EQ(1U, x86.GetPcAdjustment(0x1, elf_.get()));
- ASSERT_EQ(0U, x86.GetPcAdjustment(0x0, elf_.get()));
+ EXPECT_EQ(1U, x86.GetPcAdjustment(0x100, elf_.get()));
+ EXPECT_EQ(1U, x86.GetPcAdjustment(0x2, elf_.get()));
+ EXPECT_EQ(1U, x86.GetPcAdjustment(0x1, elf_.get()));
+ EXPECT_EQ(0U, x86.GetPcAdjustment(0x0, elf_.get()));
RegsX86_64 x86_64;
- ASSERT_EQ(1U, x86_64.GetPcAdjustment(0x100, elf_.get()));
- ASSERT_EQ(1U, x86_64.GetPcAdjustment(0x2, elf_.get()));
- ASSERT_EQ(1U, x86_64.GetPcAdjustment(0x1, elf_.get()));
- ASSERT_EQ(0U, x86_64.GetPcAdjustment(0x0, elf_.get()));
+ EXPECT_EQ(1U, x86_64.GetPcAdjustment(0x100, elf_.get()));
+ EXPECT_EQ(1U, x86_64.GetPcAdjustment(0x2, elf_.get()));
+ EXPECT_EQ(1U, x86_64.GetPcAdjustment(0x1, elf_.get()));
+ EXPECT_EQ(0U, x86_64.GetPcAdjustment(0x0, elf_.get()));
RegsMips mips;
- ASSERT_EQ(8U, mips.GetPcAdjustment(0x10, elf_.get()));
- ASSERT_EQ(8U, mips.GetPcAdjustment(0x8, elf_.get()));
- ASSERT_EQ(0U, mips.GetPcAdjustment(0x7, elf_.get()));
- ASSERT_EQ(0U, mips.GetPcAdjustment(0x6, elf_.get()));
- ASSERT_EQ(0U, mips.GetPcAdjustment(0x5, elf_.get()));
- ASSERT_EQ(0U, mips.GetPcAdjustment(0x4, elf_.get()));
- ASSERT_EQ(0U, mips.GetPcAdjustment(0x3, elf_.get()));
- ASSERT_EQ(0U, mips.GetPcAdjustment(0x2, elf_.get()));
- ASSERT_EQ(0U, mips.GetPcAdjustment(0x1, elf_.get()));
- ASSERT_EQ(0U, mips.GetPcAdjustment(0x0, elf_.get()));
+ EXPECT_EQ(8U, mips.GetPcAdjustment(0x10, elf_.get()));
+ EXPECT_EQ(8U, mips.GetPcAdjustment(0x8, elf_.get()));
+ EXPECT_EQ(0U, mips.GetPcAdjustment(0x7, elf_.get()));
+ EXPECT_EQ(0U, mips.GetPcAdjustment(0x6, elf_.get()));
+ EXPECT_EQ(0U, mips.GetPcAdjustment(0x5, elf_.get()));
+ EXPECT_EQ(0U, mips.GetPcAdjustment(0x4, elf_.get()));
+ EXPECT_EQ(0U, mips.GetPcAdjustment(0x3, elf_.get()));
+ EXPECT_EQ(0U, mips.GetPcAdjustment(0x2, elf_.get()));
+ EXPECT_EQ(0U, mips.GetPcAdjustment(0x1, elf_.get()));
+ EXPECT_EQ(0U, mips.GetPcAdjustment(0x0, elf_.get()));
RegsMips64 mips64;
- ASSERT_EQ(8U, mips64.GetPcAdjustment(0x10, elf_.get()));
- ASSERT_EQ(8U, mips64.GetPcAdjustment(0x8, elf_.get()));
- ASSERT_EQ(0U, mips64.GetPcAdjustment(0x7, elf_.get()));
- ASSERT_EQ(0U, mips64.GetPcAdjustment(0x6, elf_.get()));
- ASSERT_EQ(0U, mips64.GetPcAdjustment(0x5, elf_.get()));
- ASSERT_EQ(0U, mips64.GetPcAdjustment(0x4, elf_.get()));
- ASSERT_EQ(0U, mips64.GetPcAdjustment(0x3, elf_.get()));
- ASSERT_EQ(0U, mips64.GetPcAdjustment(0x2, elf_.get()));
- ASSERT_EQ(0U, mips64.GetPcAdjustment(0x1, elf_.get()));
- ASSERT_EQ(0U, mips64.GetPcAdjustment(0x0, elf_.get()));
+ EXPECT_EQ(8U, mips64.GetPcAdjustment(0x10, elf_.get()));
+ EXPECT_EQ(8U, mips64.GetPcAdjustment(0x8, elf_.get()));
+ EXPECT_EQ(0U, mips64.GetPcAdjustment(0x7, elf_.get()));
+ EXPECT_EQ(0U, mips64.GetPcAdjustment(0x6, elf_.get()));
+ EXPECT_EQ(0U, mips64.GetPcAdjustment(0x5, elf_.get()));
+ EXPECT_EQ(0U, mips64.GetPcAdjustment(0x4, elf_.get()));
+ EXPECT_EQ(0U, mips64.GetPcAdjustment(0x3, elf_.get()));
+ EXPECT_EQ(0U, mips64.GetPcAdjustment(0x2, elf_.get()));
+ EXPECT_EQ(0U, mips64.GetPcAdjustment(0x1, elf_.get()));
+ EXPECT_EQ(0U, mips64.GetPcAdjustment(0x0, elf_.get()));
}
TEST_F(RegsTest, rel_pc_arm) {
@@ -143,34 +143,36 @@
// Check fence posts.
elf_->FakeSetLoadBias(0);
- ASSERT_EQ(2U, arm.GetPcAdjustment(0x5, elf_.get()));
- ASSERT_EQ(0U, arm.GetPcAdjustment(0x4, elf_.get()));
- ASSERT_EQ(0U, arm.GetPcAdjustment(0x3, elf_.get()));
- ASSERT_EQ(0U, arm.GetPcAdjustment(0x2, elf_.get()));
- ASSERT_EQ(0U, arm.GetPcAdjustment(0x1, elf_.get()));
- ASSERT_EQ(0U, arm.GetPcAdjustment(0x0, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x5, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x4, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x3, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x2, elf_.get()));
+ EXPECT_EQ(0U, arm.GetPcAdjustment(0x1, elf_.get()));
+ EXPECT_EQ(0U, arm.GetPcAdjustment(0x0, elf_.get()));
elf_->FakeSetLoadBias(0x100);
- ASSERT_EQ(0U, arm.GetPcAdjustment(0xff, elf_.get()));
- ASSERT_EQ(2U, arm.GetPcAdjustment(0x105, elf_.get()));
- ASSERT_EQ(0U, arm.GetPcAdjustment(0x104, elf_.get()));
- ASSERT_EQ(0U, arm.GetPcAdjustment(0x103, elf_.get()));
- ASSERT_EQ(0U, arm.GetPcAdjustment(0x102, elf_.get()));
- ASSERT_EQ(0U, arm.GetPcAdjustment(0x101, elf_.get()));
- ASSERT_EQ(0U, arm.GetPcAdjustment(0x100, elf_.get()));
+ EXPECT_EQ(0U, arm.GetPcAdjustment(0x1, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x2, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0xff, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x105, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x104, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x103, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x102, elf_.get()));
+ EXPECT_EQ(0U, arm.GetPcAdjustment(0x101, elf_.get()));
+ EXPECT_EQ(0U, arm.GetPcAdjustment(0x100, elf_.get()));
// Check thumb instructions handling.
elf_->FakeSetLoadBias(0);
memory_->SetData32(0x2000, 0);
- ASSERT_EQ(2U, arm.GetPcAdjustment(0x2005, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x2005, elf_.get()));
memory_->SetData32(0x2000, 0xe000f000);
- ASSERT_EQ(4U, arm.GetPcAdjustment(0x2005, elf_.get()));
+ EXPECT_EQ(4U, arm.GetPcAdjustment(0x2005, elf_.get()));
elf_->FakeSetLoadBias(0x400);
memory_->SetData32(0x2100, 0);
- ASSERT_EQ(2U, arm.GetPcAdjustment(0x2505, elf_.get()));
+ EXPECT_EQ(2U, arm.GetPcAdjustment(0x2505, elf_.get()));
memory_->SetData32(0x2100, 0xf111f111);
- ASSERT_EQ(4U, arm.GetPcAdjustment(0x2505, elf_.get()));
+ EXPECT_EQ(4U, arm.GetPcAdjustment(0x2505, elf_.get()));
}
TEST_F(RegsTest, elf_invalid) {
@@ -181,32 +183,33 @@
RegsMips regs_mips;
RegsMips64 regs_mips64;
MapInfo map_info(0x1000, 0x2000);
- Elf* invalid_elf = new Elf(new MemoryFake);
+ Elf* invalid_elf = new Elf(nullptr);
map_info.elf.reset(invalid_elf);
regs_arm.set_pc(0x1500);
EXPECT_EQ(0x500U, invalid_elf->GetRelPc(regs_arm.pc(), &map_info));
- EXPECT_EQ(4U, regs_arm.GetPcAdjustment(0x500U, invalid_elf));
+ EXPECT_EQ(2U, regs_arm.GetPcAdjustment(0x500U, invalid_elf));
+ EXPECT_EQ(2U, regs_arm.GetPcAdjustment(0x511U, invalid_elf));
regs_arm64.set_pc(0x1600);
EXPECT_EQ(0x600U, invalid_elf->GetRelPc(regs_arm64.pc(), &map_info));
- EXPECT_EQ(0U, regs_arm64.GetPcAdjustment(0x600U, invalid_elf));
+ EXPECT_EQ(4U, regs_arm64.GetPcAdjustment(0x600U, invalid_elf));
regs_x86.set_pc(0x1700);
EXPECT_EQ(0x700U, invalid_elf->GetRelPc(regs_x86.pc(), &map_info));
- EXPECT_EQ(0U, regs_x86.GetPcAdjustment(0x700U, invalid_elf));
+ EXPECT_EQ(1U, regs_x86.GetPcAdjustment(0x700U, invalid_elf));
regs_x86_64.set_pc(0x1800);
EXPECT_EQ(0x800U, invalid_elf->GetRelPc(regs_x86_64.pc(), &map_info));
- EXPECT_EQ(0U, regs_x86_64.GetPcAdjustment(0x800U, invalid_elf));
+ EXPECT_EQ(1U, regs_x86_64.GetPcAdjustment(0x800U, invalid_elf));
regs_mips.set_pc(0x1900);
EXPECT_EQ(0x900U, invalid_elf->GetRelPc(regs_mips.pc(), &map_info));
- EXPECT_EQ(0U, regs_mips.GetPcAdjustment(0x900U, invalid_elf));
+ EXPECT_EQ(8U, regs_mips.GetPcAdjustment(0x900U, invalid_elf));
regs_mips64.set_pc(0x1a00);
EXPECT_EQ(0xa00U, invalid_elf->GetRelPc(regs_mips64.pc(), &map_info));
- EXPECT_EQ(0U, regs_mips64.GetPcAdjustment(0xa00U, invalid_elf));
+ EXPECT_EQ(8U, regs_mips64.GetPcAdjustment(0xa00U, invalid_elf));
}
TEST_F(RegsTest, arm_verify_sp_pc) {
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 532640f..2b8f0c2 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>
@@ -284,7 +285,7 @@
" #01 pc 00067f00 libarttestd.so (Java_Main_unwindInProcess+10032)\n"
" #02 pc 000021a8 (offset 0x2000) 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
"boolean)+136)\n"
- " #03 pc 0000fe81 anonymous:ee74c000 (boolean Main.bar(boolean)+65)\n"
+ " #03 pc 0000fe80 anonymous:ee74c000 (boolean Main.bar(boolean)+64)\n"
" #04 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
" #05 pc 00146ab5 libartd.so "
"(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
@@ -299,7 +300,7 @@
"20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
" #09 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
" #10 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
- " #11 pc 0000fe04 anonymous:ee74c000 (int Main.compare(Main, Main)+52)\n"
+ " #11 pc 0000fe03 anonymous:ee74c000 (int Main.compare(Main, Main)+51)\n"
" #12 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
" #13 pc 00146ab5 libartd.so "
"(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
@@ -314,8 +315,8 @@
"20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
" #17 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
" #18 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
- " #19 pc 0000fd3c anonymous:ee74c000 (int Main.compare(java.lang.Object, "
- "java.lang.Object)+108)\n"
+ " #19 pc 0000fd3b anonymous:ee74c000 (int Main.compare(java.lang.Object, "
+ "java.lang.Object)+107)\n"
" #20 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
" #21 pc 00146ab5 libartd.so "
"(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
@@ -330,9 +331,9 @@
"20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
" #25 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
" #26 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
- " #27 pc 0000fbdc anonymous:ee74c000 (int "
+ " #27 pc 0000fbdb anonymous:ee74c000 (int "
"java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, "
- "java.util.Comparator)+332)\n"
+ "java.util.Comparator)+331)\n"
" #28 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n"
" #29 pc 00146acb libartd.so "
"(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
@@ -347,7 +348,7 @@
"20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
" #33 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
" #34 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
- " #35 pc 0000f625 anonymous:ee74c000 (boolean Main.foo()+165)\n"
+ " #35 pc 0000f624 anonymous:ee74c000 (boolean Main.foo()+164)\n"
" #36 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
" #37 pc 00146ab5 libartd.so "
"(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
@@ -362,7 +363,7 @@
"20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
" #41 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
" #42 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
- " #43 pc 0000eedc anonymous:ee74c000 (void Main.runPrimary()+60)\n"
+ " #43 pc 0000eedb anonymous:ee74c000 (void Main.runPrimary()+59)\n"
" #44 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
" #45 pc 00146ab5 libartd.so "
"(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
@@ -377,7 +378,7 @@
"20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
" #49 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
" #50 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
- " #51 pc 0000ac22 anonymous:ee74c000 (void Main.main(java.lang.String[])+98)\n"
+ " #51 pc 0000ac21 anonymous:ee74c000 (void Main.main(java.lang.String[])+97)\n"
" #52 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n"
" #53 pc 00146acb libartd.so "
"(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
@@ -419,7 +420,7 @@
EXPECT_EQ(0xffeb52a0U, unwinder.frames()[1].sp);
EXPECT_EQ(0xec6061a8U, unwinder.frames()[2].pc);
EXPECT_EQ(0xffeb5ce0U, unwinder.frames()[2].sp);
- EXPECT_EQ(0xee75be81U, unwinder.frames()[3].pc);
+ EXPECT_EQ(0xee75be80U, unwinder.frames()[3].pc);
EXPECT_EQ(0xffeb5d30U, unwinder.frames()[3].sp);
EXPECT_EQ(0xf728e4d2U, unwinder.frames()[4].pc);
EXPECT_EQ(0xffeb5d60U, unwinder.frames()[4].sp);
@@ -435,7 +436,7 @@
EXPECT_EQ(0xffeb5fb0U, unwinder.frames()[9].sp);
EXPECT_EQ(0xf72945bdU, unwinder.frames()[10].pc);
EXPECT_EQ(0xffeb6110U, unwinder.frames()[10].sp);
- EXPECT_EQ(0xee75be04U, unwinder.frames()[11].pc);
+ EXPECT_EQ(0xee75be03U, unwinder.frames()[11].pc);
EXPECT_EQ(0xffeb6160U, unwinder.frames()[11].sp);
EXPECT_EQ(0xf728e4d2U, unwinder.frames()[12].pc);
EXPECT_EQ(0xffeb6180U, unwinder.frames()[12].sp);
@@ -451,7 +452,7 @@
EXPECT_EQ(0xffeb63e0U, unwinder.frames()[17].sp);
EXPECT_EQ(0xf72945bdU, unwinder.frames()[18].pc);
EXPECT_EQ(0xffeb6530U, unwinder.frames()[18].sp);
- EXPECT_EQ(0xee75bd3cU, unwinder.frames()[19].pc);
+ EXPECT_EQ(0xee75bd3bU, unwinder.frames()[19].pc);
EXPECT_EQ(0xffeb6580U, unwinder.frames()[19].sp);
EXPECT_EQ(0xf728e4d2U, unwinder.frames()[20].pc);
EXPECT_EQ(0xffeb65b0U, unwinder.frames()[20].sp);
@@ -467,7 +468,7 @@
EXPECT_EQ(0xffeb6810U, unwinder.frames()[25].sp);
EXPECT_EQ(0xf72945bdU, unwinder.frames()[26].pc);
EXPECT_EQ(0xffeb6960U, unwinder.frames()[26].sp);
- EXPECT_EQ(0xee75bbdcU, unwinder.frames()[27].pc);
+ EXPECT_EQ(0xee75bbdbU, unwinder.frames()[27].pc);
EXPECT_EQ(0xffeb69b0U, unwinder.frames()[27].sp);
EXPECT_EQ(0xf728e6a2U, unwinder.frames()[28].pc);
EXPECT_EQ(0xffeb69f0U, unwinder.frames()[28].sp);
@@ -483,7 +484,7 @@
EXPECT_EQ(0xffeb6c50U, unwinder.frames()[33].sp);
EXPECT_EQ(0xf72945bdU, unwinder.frames()[34].pc);
EXPECT_EQ(0xffeb6dd0U, unwinder.frames()[34].sp);
- EXPECT_EQ(0xee75b625U, unwinder.frames()[35].pc);
+ EXPECT_EQ(0xee75b624U, unwinder.frames()[35].pc);
EXPECT_EQ(0xffeb6e20U, unwinder.frames()[35].sp);
EXPECT_EQ(0xf728e4d2U, unwinder.frames()[36].pc);
EXPECT_EQ(0xffeb6e50U, unwinder.frames()[36].sp);
@@ -499,7 +500,7 @@
EXPECT_EQ(0xffeb70a0U, unwinder.frames()[41].sp);
EXPECT_EQ(0xf72945bdU, unwinder.frames()[42].pc);
EXPECT_EQ(0xffeb71f0U, unwinder.frames()[42].sp);
- EXPECT_EQ(0xee75aedcU, unwinder.frames()[43].pc);
+ EXPECT_EQ(0xee75aedbU, unwinder.frames()[43].pc);
EXPECT_EQ(0xffeb7240U, unwinder.frames()[43].sp);
EXPECT_EQ(0xf728e4d2U, unwinder.frames()[44].pc);
EXPECT_EQ(0xffeb72a0U, unwinder.frames()[44].sp);
@@ -515,7 +516,7 @@
EXPECT_EQ(0xffeb74f0U, unwinder.frames()[49].sp);
EXPECT_EQ(0xf72945bdU, unwinder.frames()[50].pc);
EXPECT_EQ(0xffeb7680U, unwinder.frames()[50].sp);
- EXPECT_EQ(0xee756c22U, unwinder.frames()[51].pc);
+ EXPECT_EQ(0xee756c21U, unwinder.frames()[51].pc);
EXPECT_EQ(0xffeb76d0U, unwinder.frames()[51].sp);
EXPECT_EQ(0xf728e6a2U, unwinder.frames()[52].pc);
EXPECT_EQ(0xffeb76f0U, unwinder.frames()[52].sp);
@@ -1070,4 +1071,44 @@
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/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/lmkd/lmkd.c b/lmkd/lmkd.c
index 45fa863..72f9f7b 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -18,8 +18,10 @@
#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>
@@ -71,6 +73,9 @@
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
#define EIGHT_MEGA (1 << 23)
+/* 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;
static bool has_inkernel_module;
@@ -267,24 +272,32 @@
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(LMKD_CTRL_PACKET packet) {
@@ -293,6 +306,8 @@
char val[20];
int soft_limit_mult;
struct lmk_procprio params;
+ bool is_system_server;
+ struct passwd *pwdrec;
lmkd_pack_get_procprio(packet, ¶ms);
@@ -304,7 +319,12 @@
snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", params.pid);
snprintf(val, sizeof(val), "%d", params.oomadj);
- writefilestring(path, val);
+ 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;
@@ -341,7 +361,15 @@
"/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);
+
+ /*
+ * 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) {
@@ -408,8 +436,8 @@
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);
}
}
@@ -727,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);
diff --git a/mkbootimg/Android.mk b/mkbootimg/Android.mk
index 8661d7d..92e1e27 100644
--- a/mkbootimg/Android.mk
+++ b/mkbootimg/Android.mk
@@ -9,3 +9,12 @@
LOCAL_MODULE := mkbootimg
include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := unpack_bootimg
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_IS_HOST_MODULE := true
+
+LOCAL_MODULE := unpack_bootimg
+
+include $(BUILD_PREBUILT)
diff --git a/mkbootimg/OWNERS b/mkbootimg/OWNERS
new file mode 100644
index 0000000..39448cf
--- /dev/null
+++ b/mkbootimg/OWNERS
@@ -0,0 +1 @@
+tbao@google.com
diff --git a/mkbootimg/bootimg.h b/mkbootimg/bootimg.h
index 60834fe..1be8c22 100644
--- a/mkbootimg/bootimg.h
+++ b/mkbootimg/bootimg.h
@@ -20,16 +20,18 @@
#ifndef _BOOT_IMAGE_H_
#define _BOOT_IMAGE_H_
-typedef struct boot_img_hdr boot_img_hdr;
-
#define BOOT_MAGIC "ANDROID!"
#define BOOT_MAGIC_SIZE 8
#define BOOT_NAME_SIZE 16
#define BOOT_ARGS_SIZE 512
#define BOOT_EXTRA_ARGS_SIZE 1024
-struct boot_img_hdr
-{
+#define BOOT_HEADER_VERSION_ZERO 0
+/*
+ * Bootloader expects the structure of boot_img_hdr with header version
+ * BOOT_HEADER_VERSION_ZERO to be as follows:
+ */
+struct boot_img_hdr_v0 {
uint8_t magic[BOOT_MAGIC_SIZE];
uint32_t kernel_size; /* size in bytes */
@@ -43,7 +45,10 @@
uint32_t tags_addr; /* physical addr for kernel tags */
uint32_t page_size; /* flash page size we assume */
- uint32_t unused; /* reserved for future expansion: MUST be 0 */
+ /*
+ * version for the boot image header.
+ */
+ uint32_t header_version;
/* operating system version and security patch level; for
* version "A.B.C" and patch level "Y-M-D":
@@ -64,31 +69,79 @@
} __attribute__((packed));
/*
-** +-----------------+
-** | boot header | 1 page
-** +-----------------+
-** | kernel | n pages
-** +-----------------+
-** | ramdisk | m pages
-** +-----------------+
-** | second stage | o pages
-** +-----------------+
-**
-** n = (kernel_size + page_size - 1) / page_size
-** m = (ramdisk_size + page_size - 1) / page_size
-** o = (second_size + page_size - 1) / page_size
-**
-** 0. all entities are page_size aligned in flash
-** 1. kernel and ramdisk are required (size != 0)
-** 2. second is optional (second_size == 0 -> no second)
-** 3. load each element (kernel, ramdisk, second) at
-** the specified physical address (kernel_addr, etc)
-** 4. prepare tags at tag_addr. kernel_args[] is
-** appended to the kernel commandline in the tags.
-** 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
-** 6. if second_size != 0: jump to second_addr
-** else: jump to kernel_addr
-*/
+ * It is expected that callers would explicitly specify which version of the
+ * boot image header they need to use.
+ */
+typedef struct boot_img_hdr_v0 boot_img_hdr;
+
+/* When a boot header is of version BOOT_HEADER_VERSION_ZERO, the structure of boot image is as
+ * follows:
+ *
+ * +-----------------+
+ * | boot header | 1 page
+ * +-----------------+
+ * | kernel | n pages
+ * +-----------------+
+ * | ramdisk | m pages
+ * +-----------------+
+ * | second stage | o pages
+ * +-----------------+
+ *
+ * n = (kernel_size + page_size - 1) / page_size
+ * m = (ramdisk_size + page_size - 1) / page_size
+ * o = (second_size + page_size - 1) / page_size
+ *
+ * 0. all entities are page_size aligned in flash
+ * 1. kernel and ramdisk are required (size != 0)
+ * 2. second is optional (second_size == 0 -> no second)
+ * 3. load each element (kernel, ramdisk, second) at
+ * the specified physical address (kernel_addr, etc)
+ * 4. prepare tags at tag_addr. kernel_args[] is
+ * appended to the kernel commandline in the tags.
+ * 5. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
+ * 6. if second_size != 0: jump to second_addr
+ * else: jump to kernel_addr
+ */
+
+#define BOOT_HEADER_VERSION_ONE 1
+
+struct boot_img_hdr_v1 : public boot_img_hdr_v0 {
+ uint32_t recovery_dtbo_size; /* size in bytes for recovery DTBO image */
+ uint64_t recovery_dtbo_offset; /* physical load addr */
+ uint32_t header_size;
+} __attribute__((packed));
+
+/* When the boot image header has a version of BOOT_HEADER_VERSION_ONE, the structure of the boot
+ * image is as follows:
+ *
+ * +-----------------+
+ * | boot header | 1 page
+ * +-----------------+
+ * | kernel | n pages
+ * +-----------------+
+ * | ramdisk | m pages
+ * +-----------------+
+ * | second stage | o pages
+ * +-----------------+
+ * | recovery dtbo | p pages
+ * +-----------------+
+ * n = (kernel_size + page_size - 1) / page_size
+ * m = (ramdisk_size + page_size - 1) / page_size
+ * o = (second_size + page_size - 1) / page_size
+ * p = (recovery_dtbo_size + page_size - 1) / page_size
+ *
+ * 0. all entities are page_size aligned in flash
+ * 1. kernel and ramdisk are required (size != 0)
+ * 2. recovery_dtbo is required for recovery.img in non-A/B devices(recovery_dtbo_size != 0)
+ * 3. second is optional (second_size == 0 -> no second)
+ * 4. load each element (kernel, ramdisk, second, recovery_dtbo) at
+ * the specified physical address (kernel_addr, etc)
+ * 5. prepare tags at tag_addr. kernel_args[] is
+ * appended to the kernel commandline in the tags.
+ * 6. r0 = 0, r1 = MACHINE_TYPE, r2 = tags_addr
+ * 7. if second_size != 0: jump to second_addr
+ * else: jump to kernel_addr
+ */
#if 0
typedef struct ptentry ptentry;
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg
index 5a13da2..ac20d05 100755
--- a/mkbootimg/mkbootimg
+++ b/mkbootimg/mkbootimg
@@ -57,7 +57,7 @@
args.base + args.second_offset, # physical load addr
args.base + args.tags_offset, # physical addr for kernel tags
args.pagesize, # flash page size we assume
- 0, # future expansion: MUST be 0
+ args.header_version, # version of bootimage header
(args.os_version << 11) | args.os_patch_level)) # os version and patch level
args.output.write(pack('16s', args.board.encode())) # asciiz product name
args.output.write(pack('512s', args.cmdline[:512].encode()))
@@ -66,10 +66,20 @@
update_sha(sha, args.kernel)
update_sha(sha, args.ramdisk)
update_sha(sha, args.second)
+
+ if args.header_version > 0:
+ update_sha(sha, args.recovery_dtbo)
+
img_id = pack('32s', sha.digest())
args.output.write(img_id)
args.output.write(pack('1024s', args.cmdline[512:].encode()))
+
+ if args.header_version > 0:
+ args.output.write(pack('I', filesize(args.recovery_dtbo))) # size in bytes
+ args.output.write(pack('Q', args.base + args.recovery_dtbo_offset)) # physical load addr
+ args.output.write(pack('I', args.output.tell() + 4)) # size of boot header
+
pad_file(args.output, args.pagesize)
return img_id
@@ -132,6 +142,7 @@
required=True)
parser.add_argument('--ramdisk', help='path to the ramdisk', type=FileType('rb'))
parser.add_argument('--second', help='path to the 2nd bootloader', type=FileType('rb'))
+ parser.add_argument('--recovery_dtbo', help='path to the recovery DTBO', type=FileType('rb'))
parser.add_argument('--cmdline', help='extra arguments to be passed on the '
'kernel command line', default='', action=ValidateStrLenAction, maxlen=1536)
parser.add_argument('--base', help='base address', type=parse_int, default=0x10000000)
@@ -139,6 +150,8 @@
parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000)
parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
default=0x00f00000)
+ parser.add_argument('--recovery_dtbo_offset', help='recovery dtbo offset', type=parse_int,
+ default=0x0f000000)
parser.add_argument('--os_version', help='operating system version', type=parse_os_version,
default=0)
parser.add_argument('--os_patch_level', help='operating system patch level',
@@ -150,6 +163,7 @@
choices=[2**i for i in range(11,15)], default=2048)
parser.add_argument('--id', help='print the image ID on standard output',
action='store_true')
+ parser.add_argument('--header_version', help='boot image header version', type=parse_int, default=0)
parser.add_argument('-o', '--output', help='output file name', type=FileType('wb'),
required=True)
return parser.parse_args()
@@ -160,6 +174,8 @@
write_padded_file(args.output, args.ramdisk, args.pagesize)
write_padded_file(args.output, args.second, args.pagesize)
+ if args.header_version > 0:
+ write_padded_file(args.output, args.recovery_dtbo, args.pagesize)
def main():
args = parse_cmdline()
diff --git a/mkbootimg/unpack_bootimg b/mkbootimg/unpack_bootimg
new file mode 100755
index 0000000..8e42ec0
--- /dev/null
+++ b/mkbootimg/unpack_bootimg
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+# Copyright 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.
+
+"""unpacks the bootimage.
+
+Extracts the kernel, ramdisk, second bootloader and recovery dtbo images.
+"""
+
+from __future__ import print_function
+from argparse import ArgumentParser, FileType
+from struct import unpack
+import os
+
+
+def create_out_dir(dir_path):
+ """creates a directory 'dir_path' if it does not exist"""
+ if not os.path.exists(dir_path):
+ os.makedirs(dir_path)
+
+
+def extract_image(offset, size, bootimage, extracted_image_name):
+ """extracts an image from the bootimage"""
+ bootimage.seek(offset)
+ with open(extracted_image_name, 'wb') as file_out:
+ file_out.write(bootimage.read(size))
+
+
+def get_number_of_pages(image_size, page_size):
+ """calculates the number of pages required for the image"""
+ return (image_size + page_size - 1) / page_size
+
+
+def unpack_bootimage(args):
+ """extracts kernel, ramdisk, second bootloader and recovery dtbo"""
+ boot_magic = unpack('8s', args.boot_img.read(8))
+ print('boot_magic: %s' % boot_magic)
+ kernel_ramdisk_second_info = unpack('10I', args.boot_img.read(10 * 4))
+ print('kernel_size: %s' % kernel_ramdisk_second_info[0])
+ print('kernel load address: %s' % kernel_ramdisk_second_info[1])
+ print('ramdisk size: %s' % kernel_ramdisk_second_info[2])
+ print('ramdisk load address: %s' % kernel_ramdisk_second_info[3])
+ print('second bootloader size: %s' % kernel_ramdisk_second_info[4])
+ print('second bootloader load address: %s' % kernel_ramdisk_second_info[5])
+ print('kernel tags load address: %s' % kernel_ramdisk_second_info[6])
+ print('page size: %s' % kernel_ramdisk_second_info[7])
+ print('boot image header version: %s' % kernel_ramdisk_second_info[8])
+ print('os version and patch level: %s' % kernel_ramdisk_second_info[9])
+
+ product_name = unpack('16s', args.boot_img.read(16))
+ print('product name: %s' % product_name)
+ cmdline = unpack('512s', args.boot_img.read(512))
+ print('command line args: %s' % cmdline)
+
+ args.boot_img.read(32) # ignore SHA
+
+ extra_cmdline = unpack('1024s', args.boot_img.read(1024))
+ print('additional command line args: %s' % extra_cmdline)
+
+ kernel_size = kernel_ramdisk_second_info[0]
+ ramdisk_size = kernel_ramdisk_second_info[2]
+ second_size = kernel_ramdisk_second_info[4]
+ page_size = kernel_ramdisk_second_info[7]
+ version = kernel_ramdisk_second_info[8]
+ if version > 0:
+ recovery_dtbo_size = unpack('I', args.boot_img.read(1 * 4))[0]
+ print('recovery dtbo size: %s' % recovery_dtbo_size)
+ recovery_dtbo_address = unpack('Q', args.boot_img.read(8))[0]
+ print('recovery dtbo load address: %s' % recovery_dtbo_address)
+ boot_header_size = unpack('I', args.boot_img.read(4))[0]
+ print('boot header size: %s' % boot_header_size)
+ else:
+ recovery_dtbo_size = 0
+
+ # The first page contains the boot header
+ num_header_pages = 1
+
+ num_kernel_pages = get_number_of_pages(kernel_size, page_size)
+ kernel_offset = page_size * num_header_pages # header occupies a page
+ image_info_list = [(kernel_offset, kernel_size, 'kernel')]
+
+ num_ramdisk_pages = get_number_of_pages(ramdisk_size, page_size)
+ ramdisk_offset = page_size * (num_header_pages + num_kernel_pages
+ ) # header + kernel
+ image_info_list.append((ramdisk_offset, ramdisk_size, 'ramdisk'))
+
+ num_second_pages = get_number_of_pages(second_size, page_size)
+ second_offset = page_size * (
+ num_header_pages + num_kernel_pages + num_ramdisk_pages
+ ) # header + kernel + ramdisk
+ image_info_list.append((second_offset, second_size, 'second'))
+
+ if recovery_dtbo_size > 0:
+ dtbo_offset = page_size * (num_header_pages + num_kernel_pages +
+ num_ramdisk_pages + num_second_pages)
+ image_info_list.append((dtbo_offset, recovery_dtbo_size,
+ 'recovery_dtbo'))
+
+ for image_info in image_info_list:
+ extract_image(image_info[0], image_info[1], args.boot_img,
+ os.path.join(args.out, image_info[2]))
+
+
+def parse_cmdline():
+ """parse command line arguments"""
+ parser = ArgumentParser(
+ description='Unpacks boot.img/recovery.img, extracts the kernel,'
+ 'ramdisk, second bootloader and recovery dtbo')
+ parser.add_argument(
+ '--boot_img',
+ help='path to boot image',
+ type=FileType('rb'),
+ required=True)
+ parser.add_argument('--out', help='path to out binaries', default='out')
+ return parser.parse_args()
+
+
+def main():
+ """parse arguments and unpack boot image"""
+ args = parse_cmdline()
+ create_out_dir(args.out)
+ unpack_bootimage(args)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/property_service/property_info_checker/Android.bp b/property_service/property_info_checker/Android.bp
index 6ee649a..7d66199 100644
--- a/property_service/property_info_checker/Android.bp
+++ b/property_service/property_info_checker/Android.bp
@@ -7,6 +7,7 @@
"libpropertyinfoserializer",
"libpropertyinfoparser",
"libbase",
+ "libsepol",
],
srcs: ["property_info_checker.cpp"],
}
diff --git a/property_service/property_info_checker/property_info_checker.cpp b/property_service/property_info_checker/property_info_checker.cpp
index e4f8264..52c4383 100644
--- a/property_service/property_info_checker/property_info_checker.cpp
+++ b/property_service/property_info_checker/property_info_checker.cpp
@@ -1,26 +1,150 @@
#include <iostream>
+#include <memory>
#include <string>
#include <vector>
#include <android-base/file.h>
-
+#include <property_info_parser/property_info_parser.h>
#include <property_info_serializer/property_info_serializer.h>
+#include <sepol/context.h>
+#include <sepol/context_record.h>
+#include <sepol/handle.h>
+#include <sepol/policydb.h>
+#include <sepol/policydb/policydb.h>
using android::base::ReadFileToString;
using android::properties::BuildTrie;
using android::properties::ParsePropertyInfoFile;
+using android::properties::PropertyInfoArea;
using android::properties::PropertyInfoEntry;
+class ContextChecker {
+ public:
+ ContextChecker()
+ : policy_file_(nullptr),
+ sepol_handle_(nullptr),
+ sepol_policy_file_(nullptr),
+ sepol_policy_db_(nullptr) {}
+
+ ~ContextChecker() {
+ if (sepol_policy_db_ != nullptr) {
+ sepol_policydb_free(sepol_policy_db_);
+ }
+
+ if (sepol_policy_file_ != nullptr) {
+ sepol_policy_file_free(sepol_policy_file_);
+ }
+
+ if (sepol_handle_ != nullptr) {
+ sepol_handle_destroy(sepol_handle_);
+ }
+
+ if (policy_file_ != nullptr) {
+ fclose(policy_file_);
+ }
+ }
+
+ bool Initialize(const char* policy_file) {
+ policy_file_ = fopen(policy_file, "re");
+ if (policy_file_ == nullptr) {
+ std::cerr << "Could not open policy file, " << policy_file << std::endl;
+ return false;
+ }
+
+ sepol_handle_ = sepol_handle_create();
+ if (sepol_handle_ == nullptr) {
+ std::cerr << "Could not create policy handle." << std::endl;
+ return false;
+ }
+
+ if (sepol_policy_file_create(&sepol_policy_file_) < 0) {
+ std::cerr << "Could not create policy file." << std::endl;
+ return false;
+ }
+
+ if (sepol_policydb_create(&sepol_policy_db_) < 0) {
+ std::cerr << "Could not create policy db." << std::endl;
+ return false;
+ }
+
+ sepol_policy_file_set_fp(sepol_policy_file_, policy_file_);
+ sepol_policy_file_set_handle(sepol_policy_file_, sepol_handle_);
+
+ if (sepol_policydb_read(sepol_policy_db_, sepol_policy_file_) < 0) {
+ std::cerr << "Could not read policy file into policy db." << std::endl;
+ return false;
+ }
+
+ auto* attr =
+ reinterpret_cast<type_datum*>(hashtab_search(policy_db_->p_types.table, "property_type"));
+ if (attr == nullptr || attr->flavor != TYPE_ATTRIB) {
+ std::cerr << "'property_type' is not defined correctly." << std::endl;
+ return false;
+ }
+
+ property_type_bit_ = attr->s.value - 1;
+
+ return true;
+ }
+
+ bool CheckContext(const char* context) {
+ sepol_context_t* sepol_context_raw;
+ if (sepol_context_from_string(sepol_handle_, context, &sepol_context_raw) < 0) {
+ std::cerr << "Could not allocate context for " << context << std::endl;
+ return false;
+ }
+ auto sepol_context = std::unique_ptr<sepol_context_t, decltype(&sepol_context_free)>{
+ sepol_context_raw, sepol_context_free};
+
+ if (sepol_context_check(sepol_handle_, sepol_policy_db_, sepol_context.get()) < 0) {
+ std::cerr << "Sepol context check failed for " << context << std::endl;
+ return false;
+ }
+
+ const char* context_type = sepol_context_get_type(sepol_context.get());
+
+ auto* type =
+ reinterpret_cast<type_datum*>(hashtab_search(policy_db_->p_types.table, context_type));
+ if (type == nullptr) {
+ std::cerr << "Could not find context '" << context << "' in policy database" << std::endl;
+ return false;
+ }
+
+ if (type->flavor != TYPE_TYPE) {
+ std::cerr << "Context '" << context << "' is not defined as a type in policy database"
+ << std::endl;
+ return false;
+ }
+
+ if (!ebitmap_get_bit(&policy_db_->type_attr_map[type->s.value - 1], property_type_bit_)) {
+ std::cerr << "Context '" << context << "' does not have property_type attribute" << std::endl;
+ return false;
+ }
+
+ return true;
+ }
+
+ private:
+ FILE* policy_file_;
+ sepol_handle_t* sepol_handle_;
+ sepol_policy_file_t* sepol_policy_file_;
+ union {
+ sepol_policydb_t* sepol_policy_db_;
+ policydb_t* policy_db_;
+ };
+ unsigned int property_type_bit_;
+};
+
int main(int argc, char** argv) {
- if (argc < 2) {
- std::cerr << "A list of property info files to be checked is expected on the command line"
- << std::endl;
+ if (argc < 3) {
+ std::cerr << "usage: " << argv[0]
+ << " COMPILED_SEPOLICY PROPERTY_INFO_FILE [PROPERTY_INFO_FILE]..." << std::endl;
return -1;
}
auto property_info_entries = std::vector<PropertyInfoEntry>{};
- for (int i = 1; i < argc; ++i) {
+ for (int i = 2; i < argc; ++i) {
auto filename = argv[i];
auto file_contents = std::string{};
if (!ReadFileToString(filename, &file_contents)) {
@@ -47,5 +171,17 @@
return -1;
}
+ auto checker = ContextChecker{};
+ if (!checker.Initialize(argv[1])) {
+ return -1;
+ }
+
+ auto property_info_area = reinterpret_cast<PropertyInfoArea*>(serialized_contexts.data());
+ for (size_t i = 0; i < property_info_area->num_contexts(); ++i) {
+ if (!checker.CheckContext(property_info_area->context(i))) {
+ return -1;
+ }
+ }
+
return 0;
}
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