Merge "do not start shutdown animation from init"
diff --git a/adb/adb.cpp b/adb/adb.cpp
index cf6b359..39e71e5 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -253,6 +253,19 @@
send_packet(cp, t);
}
+#if ADB_HOST
+
+void SendConnectOnHost(atransport* t) {
+ // Send an empty message before A_CNXN message. This is because the data toggle of the ep_out on
+ // host and ep_in on device may not be the same.
+ apacket* p = get_apacket();
+ CHECK(p);
+ send_packet(p, t);
+ send_connect(t);
+}
+
+#endif
+
// qual_overwrite is used to overwrite a qualifier string. dst is a
// pointer to a char pointer. It is assumed that if *dst is non-NULL, it
// was malloc'ed and needs to freed. *dst will be set to a dup of src.
@@ -299,29 +312,29 @@
const std::string& type = pieces[0];
if (type == "bootloader") {
D("setting connection_state to kCsBootloader");
- t->connection_state = kCsBootloader;
+ t->SetConnectionState(kCsBootloader);
update_transports();
} else if (type == "device") {
D("setting connection_state to kCsDevice");
- t->connection_state = kCsDevice;
+ t->SetConnectionState(kCsDevice);
update_transports();
} else if (type == "recovery") {
D("setting connection_state to kCsRecovery");
- t->connection_state = kCsRecovery;
+ t->SetConnectionState(kCsRecovery);
update_transports();
} else if (type == "sideload") {
D("setting connection_state to kCsSideload");
- t->connection_state = kCsSideload;
+ t->SetConnectionState(kCsSideload);
update_transports();
} else {
D("setting connection_state to kCsHost");
- t->connection_state = kCsHost;
+ t->SetConnectionState(kCsHost);
}
}
static void handle_new_connection(atransport* t, apacket* p) {
- if (t->connection_state != kCsOffline) {
- t->connection_state = kCsOffline;
+ if (t->GetConnectionState() != kCsOffline) {
+ t->SetConnectionState(kCsOffline);
handle_offline(t);
}
@@ -355,10 +368,10 @@
if (p->msg.arg0){
send_packet(p, t);
#if ADB_HOST
- send_connect(t);
+ SendConnectOnHost(t);
#endif
} else {
- t->connection_state = kCsOffline;
+ t->SetConnectionState(kCsOffline);
handle_offline(t);
send_packet(p, t);
}
@@ -372,7 +385,9 @@
switch (p->msg.arg0) {
#if ADB_HOST
case ADB_AUTH_TOKEN:
- t->connection_state = kCsUnauthorized;
+ if (t->GetConnectionState() == kCsOffline) {
+ t->SetConnectionState(kCsUnauthorized);
+ }
send_auth_response(p->data, p->msg.data_length, t);
break;
#else
@@ -391,7 +406,7 @@
break;
#endif
default:
- t->connection_state = kCsOffline;
+ t->SetConnectionState(kCsOffline);
handle_offline(t);
break;
}
@@ -507,8 +522,8 @@
if (!_try_make_handle_noninheritable(h)) {
// Show the handle value to give us a clue in case we have problems
// with pseudo-handle values.
- fprintf(stderr, "Cannot make handle 0x%p non-inheritable: %s\n",
- h, android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ fprintf(stderr, "adb: cannot make handle 0x%p non-inheritable: %s\n", h,
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
return false;
}
@@ -523,7 +538,7 @@
HANDLE pipe_read_raw = NULL;
HANDLE pipe_write_raw = NULL;
if (!CreatePipe(&pipe_read_raw, &pipe_write_raw, sa, 0)) {
- fprintf(stderr, "Cannot create pipe: %s\n",
+ fprintf(stderr, "adb: CreatePipe failed: %s\n",
android::base::SystemErrorCodeToString(GetLastError()).c_str());
return false;
}
@@ -554,7 +569,8 @@
std::unique_ptr<FILE, decltype(&fclose)> stream(nullptr, fclose);
if (original_fd == -1) {
- fprintf(stderr, "Failed to get file descriptor for %s: %s\n", output_name, strerror(errno));
+ fprintf(stderr, "adb: failed to get file descriptor for %s: %s\n", output_name,
+ strerror(errno));
return EXIT_FAILURE;
}
@@ -566,7 +582,7 @@
// call this function if subprocesses may be started concurrently.
const int fd = dup(original_fd);
if (fd == -1) {
- fprintf(stderr, "Failed to duplicate file descriptor for %s: %s\n", output_name,
+ fprintf(stderr, "adb: failed to duplicate file descriptor for %s: %s\n", output_name,
strerror(errno));
return EXIT_FAILURE;
}
@@ -574,7 +590,7 @@
// Note that although we call fdopen() below with a binary flag, it may not adhere to that
// flag, so we have to set the mode manually.
if (_setmode(fd, _O_BINARY) == -1) {
- fprintf(stderr, "Failed to set binary mode for duplicate of %s: %s\n", output_name,
+ fprintf(stderr, "adb: failed to set binary mode for duplicate of %s: %s\n", output_name,
strerror(errno));
unix_close(fd);
return EXIT_FAILURE;
@@ -582,7 +598,7 @@
stream.reset(fdopen(fd, "wb"));
if (stream.get() == nullptr) {
- fprintf(stderr, "Failed to open duplicate stream for %s: %s\n", output_name,
+ fprintf(stderr, "adb: failed to open duplicate stream for %s: %s\n", output_name,
strerror(errno));
unix_close(fd);
return EXIT_FAILURE;
@@ -591,7 +607,7 @@
// Unbuffer the stream because it will be buffered by default and we want subprocess output
// to be shown immediately.
if (setvbuf(stream.get(), NULL, _IONBF, 0) == -1) {
- fprintf(stderr, "Failed to unbuffer %s: %s\n", output_name, strerror(errno));
+ fprintf(stderr, "adb: failed to unbuffer %s: %s\n", output_name, strerror(errno));
return EXIT_FAILURE;
}
@@ -608,7 +624,7 @@
if (err == ERROR_BROKEN_PIPE) {
return EXIT_SUCCESS;
} else {
- fprintf(stderr, "Failed to read from %s: %s\n", output_name,
+ fprintf(stderr, "adb: failed to read from %s: %s\n", output_name,
android::base::SystemErrorCodeToString(err).c_str());
return EXIT_FAILURE;
}
@@ -619,8 +635,8 @@
// fwrite() actually calls adb_fwrite() which can write UTF-8 to the console.
const size_t bytes_written = fwrite(buf, 1, bytes_read, stream.get());
if (bytes_written != bytes_read) {
- fprintf(stderr, "Only wrote %zu of %lu bytes to %s\n", bytes_written, bytes_read,
- output_name);
+ fprintf(stderr, "adb: error: only wrote %zu of %lu bytes to %s\n", bytes_written,
+ bytes_read, output_name);
return EXIT_FAILURE;
}
}
@@ -663,7 +679,7 @@
FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL));
if (nul_read.get() == INVALID_HANDLE_VALUE) {
- fprintf(stderr, "Cannot open 'nul': %s\n",
+ fprintf(stderr, "adb: CreateFileW 'nul' failed: %s\n",
android::base::SystemErrorCodeToString(GetLastError()).c_str());
return -1;
}
@@ -725,8 +741,7 @@
// If this fires, either handle values are larger than 32-bits or else
// there is a bug in our casting.
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa384203%28v=vs.85%29.aspx
- fprintf(stderr, "Cannot fit pipe handle value into 32-bits: 0x%p\n",
- ack_write.get());
+ fprintf(stderr, "adb: cannot fit pipe handle value into 32-bits: 0x%p\n", ack_write.get());
return -1;
}
@@ -736,7 +751,7 @@
arraysize(program_path));
if ((module_result >= arraysize(program_path)) || (module_result == 0)) {
// String truncation or some other error.
- fprintf(stderr, "Cannot get executable path: %s\n",
+ fprintf(stderr, "adb: cannot get executable path: %s\n",
android::base::SystemErrorCodeToString(GetLastError()).c_str());
return -1;
}
@@ -761,7 +776,7 @@
NULL, /* use parent's starting directory */
&startup, /* startup info, i.e. std handles */
&pinfo )) {
- fprintf(stderr, "Cannot create process: %s\n",
+ fprintf(stderr, "adb: CreateProcessW failed: %s\n",
android::base::SystemErrorCodeToString(GetLastError()).c_str());
return -1;
}
@@ -791,7 +806,7 @@
_beginthreadex(NULL, 0, _redirect_stdout_thread, stdout_read.get(),
0, NULL)));
if (stdout_thread.get() == nullptr) {
- fprintf(stderr, "Cannot create thread: %s\n", strerror(errno));
+ fprintf(stderr, "adb: cannot create thread: %s\n", strerror(errno));
return -1;
}
stdout_read.release(); // Transfer ownership to new thread
@@ -800,7 +815,7 @@
_beginthreadex(NULL, 0, _redirect_stderr_thread, stderr_read.get(),
0, NULL)));
if (stderr_thread.get() == nullptr) {
- fprintf(stderr, "Cannot create thread: %s\n", strerror(errno));
+ fprintf(stderr, "adb: cannot create thread: %s\n", strerror(errno));
return -1;
}
stderr_read.release(); // Transfer ownership to new thread
@@ -845,22 +860,20 @@
if (wait_result == WAIT_TIMEOUT) {
// Threads did not finish after waiting a little while. Perhaps the
// server didn't close pipes, or it is hung.
- fprintf(stderr, "Timed-out waiting for threads to finish reading from "
- "ADB Server\n");
+ fprintf(stderr, "adb: timed out waiting for threads to finish reading from ADB server\n");
// Process handles are signaled when the process exits, so if we wait
// on the handle for 0 seconds and it returns 'timeout', that means that
// the process is still running.
if (WaitForSingleObject(process_handle.get(), 0) == WAIT_TIMEOUT) {
// We could TerminateProcess(), but that seems somewhat presumptive.
- fprintf(stderr, "ADB Server is running: process id %lu\n",
- pinfo.dwProcessId);
+ fprintf(stderr, "adb: server is running with process id %lu\n", pinfo.dwProcessId);
}
return -1;
}
if (wait_result != WAIT_OBJECT_0) {
- fprintf(stderr, "Unexpected result waiting for threads: %lu: %s\n",
- wait_result, android::base::SystemErrorCodeToString(GetLastError()).c_str());
+ fprintf(stderr, "adb: unexpected result waiting for threads: %lu: %s\n", wait_result,
+ android::base::SystemErrorCodeToString(GetLastError()).c_str());
return -1;
}
@@ -894,7 +907,7 @@
int result = execl(path.c_str(), "adb", "-L", socket_spec.c_str(), "fork-server", "server",
"--reply-fd", reply_fd, NULL);
// this should not return
- fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno);
+ fprintf(stderr, "adb: execl returned %d: %s\n", result, strerror(errno));
} else {
// parent side of the fork
@@ -1034,7 +1047,6 @@
SendProtocolString(fd, s);
return 0;
}
-#endif
int handle_host_request(const char* service, TransportType type,
const char* serial, int reply_fd, asocket* s) {
@@ -1053,7 +1065,6 @@
android::base::quick_exit(0);
}
-#if ADB_HOST
// "transport:" is used for switching transport with a specified serial number
// "transport-usb:" is used for switching transport to the only USB transport
// "transport-local:" is used for switching transport to the only local transport
@@ -1098,16 +1109,10 @@
if (!strcmp(service, "reconnect-offline")) {
std::string response;
close_usb_devices([&response](const atransport* transport) {
- switch (transport->connection_state) {
+ switch (transport->GetConnectionState()) {
case kCsOffline:
case kCsUnauthorized:
- response += "reconnecting ";
- if (transport->serial) {
- response += transport->serial;
- } else {
- response += "<unknown>";
- }
- response += "\n";
+ response += "reconnecting " + transport->serial_name() + "\n";
return true;
default:
return false;
@@ -1131,7 +1136,6 @@
return 0;
}
-#if ADB_HOST
if (!strcmp(service, "host-features")) {
FeatureSet features = supported_features();
// Abuse features to report libusb status.
@@ -1141,7 +1145,6 @@
SendOkay(reply_fd, FeatureSetToString(features));
return 0;
}
-#endif
// remove TCP transport
if (!strncmp(service, "disconnect:", 11)) {
@@ -1211,15 +1214,19 @@
}
if (!strcmp(service, "reconnect")) {
- if (s->transport != nullptr) {
- kick_transport(s->transport);
+ std::string response;
+ atransport* t = acquire_one_transport(type, serial, nullptr, &response, true);
+ if (t != nullptr) {
+ kick_transport(t);
+ response =
+ "reconnecting " + t->serial_name() + " [" + t->connection_state_name() + "]\n";
}
- return SendOkay(reply_fd, "done");
+ return SendOkay(reply_fd, response);
}
-#endif // ADB_HOST
int ret = handle_forward_request(service, type, serial, reply_fd);
if (ret >= 0)
return ret - 1;
return -1;
}
+#endif // ADB_HOST
diff --git a/adb/adb.h b/adb/adb.h
index aea5fb8..e3675d8 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -139,7 +139,7 @@
int get_available_local_transport_index();
#endif
int init_socket_transport(atransport *t, int s, int port, int local);
-void init_usb_transport(atransport *t, usb_handle *usb, ConnectionState state);
+void init_usb_transport(atransport* t, usb_handle* usb);
std::string getEmulatorSerialString(int console_port);
#if ADB_HOST
@@ -222,6 +222,9 @@
void handle_offline(atransport *t);
void send_connect(atransport *t);
+#if ADB_HOST
+void SendConnectOnHost(atransport* t);
+#endif
void parse_banner(const std::string&, atransport* t);
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index ef52189..b656887 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -136,8 +136,7 @@
return -2;
}
- if ((memcmp(&service[0],"host",4) != 0 || service == "host:reconnect") &&
- switch_socket_transport(fd, error)) {
+ if (memcmp(&service[0], "host", 4) != 0 && switch_socket_transport(fd, error)) {
return -1;
}
@@ -147,11 +146,9 @@
return -1;
}
- if (service != "reconnect") {
- if (!adb_status(fd, error)) {
- adb_close(fd);
- return -1;
- }
+ if (!adb_status(fd, error)) {
+ adb_close(fd);
+ return -1;
}
D("_adb_connect: return fd %d", fd);
@@ -164,21 +161,21 @@
D("adb_connect: service %s", service.c_str());
if (fd == -2 && !is_local_socket_spec(__adb_server_socket_spec)) {
- fprintf(stderr,"** Cannot start server on remote host\n");
+ fprintf(stderr, "* cannot start server on remote host\n");
// error is the original network connection error
return fd;
} else if (fd == -2) {
- fprintf(stdout, "* daemon not running. starting it now at %s *\n", __adb_server_socket_spec);
+ fprintf(stderr, "* daemon not running; starting now at %s\n", __adb_server_socket_spec);
start_server:
if (launch_server(__adb_server_socket_spec)) {
- fprintf(stderr,"* failed to start daemon *\n");
+ fprintf(stderr, "* failed to start daemon\n");
// launch_server() has already printed detailed error info, so just
// return a generic error string about the overall adb_connect()
// that the caller requested.
*error = "cannot connect to daemon";
return -1;
} else {
- fprintf(stdout,"* daemon started successfully *\n");
+ fprintf(stderr, "* daemon started successfully\n");
}
// Give the server some time to start properly and detect devices.
std::this_thread::sleep_for(3s);
@@ -213,8 +210,8 @@
}
if (version != ADB_SERVER_VERSION) {
- printf("adb server version (%d) doesn't match this client (%d); killing...\n",
- version, ADB_SERVER_VERSION);
+ fprintf(stderr, "adb server version (%d) doesn't match this client (%d); killing...\n",
+ version, ADB_SERVER_VERSION);
fd = _adb_connect("host:kill", error);
if (fd >= 0) {
ReadOrderlyShutdown(fd);
@@ -240,7 +237,7 @@
if (fd == -1) {
D("_adb_connect error: %s", error->c_str());
} else if(fd == -2) {
- fprintf(stderr,"** daemon still not running\n");
+ fprintf(stderr, "* daemon still not running\n");
}
D("adb_connect: return fd %d", fd);
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index c369d60..eac923d 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -155,7 +155,7 @@
}
#endif
-#if !defined(_WIN32)
+#if ADB_HOST && !defined(_WIN32)
// adb historically ignored $ANDROID_LOG_TAGS but passed it through to logcat.
// If set, move it out of the way so that libbase logging doesn't try to parse it.
std::string log_tags;
@@ -168,7 +168,7 @@
android::base::InitLogging(argv, &AdbLogger);
-#if !defined(_WIN32)
+#if ADB_HOST && !defined(_WIN32)
// Put $ANDROID_LOG_TAGS back so we can pass it to logcat.
if (!log_tags.empty()) setenv("ANDROID_LOG_TAGS", log_tags.c_str(), 1);
#endif
diff --git a/adb/adb_trace.h b/adb/adb_trace.h
index aaffa29..fc6560c 100644
--- a/adb/adb_trace.h
+++ b/adb/adb_trace.h
@@ -58,6 +58,9 @@
void adb_trace_init(char**);
void adb_trace_enable(AdbTrace trace_tag);
+// Include <atomic> before stdatomic.h (introduced in cutils/trace.h) to avoid compile error.
+#include <atomic>
+
#define ATRACE_TAG ATRACE_TAG_ADB
#include <cutils/trace.h>
#include <utils/Trace.h>
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 31d3dc6..6f2403d 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -267,8 +267,8 @@
adb_close(fd);
}
-int usage(const char* fmt, ...) {
- fprintf(stderr, "adb: ");
+int syntax_error(const char* fmt, ...) {
+ fprintf(stderr, "adb: usage: ");
va_list ap;
va_start(ap, fmt);
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index e0ad103..c1d5549 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -21,7 +21,7 @@
#include <android-base/macros.h>
-int usage(const char*, ...);
+int syntax_error(const char*, ...);
void close_stdin();
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
index 9dc9811..da2cfa2 100644
--- a/adb/bugreport.cpp
+++ b/adb/bugreport.cpp
@@ -137,7 +137,7 @@
SetSrcFile(&line[strlen(BUGZ_OK_PREFIX)]);
} else if (android::base::StartsWith(line, BUGZ_FAIL_PREFIX)) {
const char* error_message = &line[strlen(BUGZ_FAIL_PREFIX)];
- fprintf(stderr, "Device failed to take a zipped bugreport: %s\n", error_message);
+ fprintf(stderr, "adb: device failed to take a zipped bugreport: %s\n", error_message);
status_ = -1;
} else if (show_progress_ && android::base::StartsWith(line, BUGZ_PROGRESS_PREFIX)) {
// progress_line should have the following format:
@@ -195,7 +195,7 @@
};
int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) {
- if (argc > 2) return usage("usage: adb bugreport [PATH]");
+ if (argc > 2) return syntax_error("adb bugreport [PATH]");
// Gets bugreportz version.
std::string bugz_stdout, bugz_stderr;
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index c48a251..fec4742 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -62,12 +62,11 @@
using unique_device_handle = std::unique_ptr<libusb_device_handle, DeviceHandleDeleter>;
struct transfer_info {
- transfer_info(const char* name, uint16_t zero_mask) :
- name(name),
- transfer(libusb_alloc_transfer(0)),
- zero_mask(zero_mask)
- {
- }
+ transfer_info(const char* name, uint16_t zero_mask, bool is_bulk_out)
+ : name(name),
+ transfer(libusb_alloc_transfer(0)),
+ is_bulk_out(is_bulk_out),
+ zero_mask(zero_mask) {}
~transfer_info() {
libusb_free_transfer(transfer);
@@ -75,6 +74,7 @@
const char* name;
libusb_transfer* transfer;
+ bool is_bulk_out;
bool transfer_complete;
std::condition_variable cv;
std::mutex mutex;
@@ -96,12 +96,11 @@
serial(serial),
closing(false),
device_handle(device_handle.release()),
- read("read", zero_mask),
- write("write", zero_mask),
+ read("read", zero_mask, false),
+ write("write", zero_mask, true),
interface(interface),
bulk_in(bulk_in),
- bulk_out(bulk_out) {
- }
+ bulk_out(bulk_out) {}
~usb_handle() {
Close();
@@ -365,11 +364,6 @@
device_poll_thread = new std::thread(poll_for_devices);
android::base::at_quick_exit([]() {
terminate_device_poll_thread = true;
- std::unique_lock<std::mutex> lock(usb_handles_mutex);
- for (auto& it : usb_handles) {
- it.second->Close();
- }
- lock.unlock();
device_poll_thread->join();
});
}
@@ -397,7 +391,8 @@
return;
}
- if (transfer->actual_length != transfer->length) {
+ // usb_read() can return when receiving some data.
+ if (info->is_bulk_out && transfer->actual_length != transfer->length) {
LOG(DEBUG) << info->name << " transfer incomplete, resubmitting";
transfer->length -= transfer->actual_length;
transfer->buffer += transfer->actual_length;
@@ -491,8 +486,12 @@
info->transfer->num_iso_packets = 0;
int rc = perform_usb_transfer(h, info, std::move(lock));
- LOG(DEBUG) << "usb_read(" << len << ") = " << rc;
- return rc;
+ LOG(DEBUG) << "usb_read(" << len << ") = " << rc << ", actual_length "
+ << info->transfer->actual_length;
+ if (rc < 0) {
+ return rc;
+ }
+ return info->transfer->actual_length;
}
int usb_close(usb_handle* h) {
diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp
index 3a45dbd..6efed27 100644
--- a/adb/client/usb_linux.cpp
+++ b/adb/client/usb_linux.cpp
@@ -401,7 +401,6 @@
}
}
-
int usb_write(usb_handle *h, const void *_data, int len)
{
D("++ usb_write ++");
@@ -429,19 +428,16 @@
int n;
D("++ usb_read ++");
- while(len > 0) {
+ int orig_len = len;
+ while (len == orig_len) {
int xfer = len;
D("[ usb read %d fd = %d], path=%s", xfer, h->fd, h->path.c_str());
n = usb_bulk_read(h, data, xfer);
D("[ usb read %d ] = %d, path=%s", xfer, n, h->path.c_str());
- if(n != xfer) {
+ if (n <= 0) {
if((errno == ETIMEDOUT) && (h->fd != -1)) {
D("[ timeout ]");
- if(n > 0){
- data += n;
- len -= n;
- }
continue;
}
D("ERROR: n = %d, errno = %d (%s)",
@@ -449,12 +445,12 @@
return -1;
}
- len -= xfer;
- data += xfer;
+ len -= n;
+ data += n;
}
D("-- usb_read --");
- return 0;
+ return orig_len - len;
}
void usb_kick(usb_handle* h) {
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index 8713b2c..fcd0bc0 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -518,7 +518,7 @@
}
if (kIOReturnSuccess == result)
- return 0;
+ return numBytes;
else {
LOG(ERROR) << "usb_read failed with status: " << std::hex << result;
}
diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp
index 9e00a5d..ee7f802 100644
--- a/adb/client/usb_windows.cpp
+++ b/adb/client/usb_windows.cpp
@@ -415,6 +415,7 @@
unsigned long time_out = 0;
unsigned long read = 0;
int err = 0;
+ int orig_len = len;
D("usb_read %d", len);
if (NULL == handle) {
@@ -423,9 +424,8 @@
goto fail;
}
- while (len > 0) {
- if (!AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read,
- time_out)) {
+ while (len == orig_len) {
+ if (!AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read, time_out)) {
D("AdbReadEndpointSync failed: %s",
android::base::SystemErrorCodeToString(GetLastError()).c_str());
err = EIO;
@@ -433,11 +433,11 @@
}
D("usb_read got: %ld, expected: %d", read, len);
- data = (char *)data + read;
+ data = (char*)data + read;
len -= read;
}
- return 0;
+ return orig_len - len;
fail:
// Any failure should cause us to kick the device instead of leaving it a
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 7702b0e..5f55ab9 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -212,6 +212,7 @@
" kill-server kill the server if it is running\n"
" reconnect kick connection from host side to force reconnect\n"
" reconnect device kick connection from device side to force reconnect\n"
+ " reconnect offline reset offline/unauthorized devices to force reconnect\n"
"\n"
"environment variables:\n"
" $ADB_TRACE\n"
@@ -690,8 +691,7 @@
switch (opt) {
case 'e':
if (!(strlen(optarg) == 1 || strcmp(optarg, "none") == 0)) {
- fprintf(stderr, "error: -e requires a single-character argument or 'none'\n");
- return 1;
+ return syntax_error("-e requires a single-character argument or 'none'");
}
escape_char = (strcmp(optarg, "none") == 0) ? 0 : optarg[0];
break;
@@ -843,7 +843,10 @@
* we hang up.
*/
static int adb_sideload_host(const char* filename) {
- fprintf(stderr, "opening '%s'...\n", filename);
+ // TODO: use a LinePrinter instead...
+ fprintf(stdout, "opening '%s'...\n", filename);
+ fflush(stdout);
+
struct stat sb;
if (stat(filename, &sb) == -1) {
fprintf(stderr, "failed to stat file %s: %s\n", filename, strerror(errno));
@@ -855,7 +858,8 @@
return -1;
}
- fprintf(stderr, "connecting...\n");
+ fprintf(stdout, "connecting...\n");
+ fflush(stdout);
std::string service = android::base::StringPrintf(
"sideload-host:%d:%d", static_cast<int>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
std::string error;
@@ -944,19 +948,13 @@
fprintf(stderr, "error: adb %s not implemented on Win32\n", argv[0]);
return -1;
#else
- if (argc < 2) {
- fprintf(stderr, "usage: adb %s <adb service name> [ppp opts]\n",
- argv[0]);
-
- return 1;
- }
+ if (argc < 2) return syntax_error("adb %s <adb service name> [ppp opts]", argv[0]);
const char* adb_service_name = argv[1];
std::string error;
int fd = adb_connect(adb_service_name, &error);
if (fd < 0) {
- fprintf(stderr,"Error: Could not open adb service: %s. Error: %s\n",
- adb_service_name, error.c_str());
+ fprintf(stderr, "adb: could not open adb service %s: %s\n", adb_service_name, error.c_str());
return 1;
}
@@ -1175,10 +1173,7 @@
/* find, extract, and use any -f argument */
for (int i = 1; i < argc; i++) {
if (!strcmp("-f", argv[i])) {
- if (i == argc-1) {
- fprintf(stderr, "adb: backup -f passed with no filename.\n");
- return EXIT_FAILURE;
- }
+ if (i == argc - 1) return syntax_error("backup -f passed with no filename");
filename = argv[i+1];
for (int j = i+2; j <= argc; ) {
argv[i++] = argv[j++];
@@ -1190,10 +1185,7 @@
// Bare "adb backup" or "adb backup -f filename" are not valid invocations ---
// a list of packages is required.
- if (argc < 2) {
- fprintf(stderr, "adb: backup either needs a list of packages or -all/-shared.\n");
- return EXIT_FAILURE;
- }
+ if (argc < 2) return syntax_error("backup either needs a list of packages or -all/-shared");
adb_unlink(filename);
int outFd = adb_creat(filename, 0640);
@@ -1218,7 +1210,7 @@
return EXIT_FAILURE;
}
- printf("Now unlock your device and confirm the backup operation...\n");
+ fprintf(stdout, "Now unlock your device and confirm the backup operation...\n");
fflush(stdout);
copy_to_file(fd, outFd);
@@ -1229,7 +1221,7 @@
}
static int restore(int argc, const char** argv) {
- if (argc != 2) return usage("restore requires an argument");
+ if (argc != 2) return syntax_error("restore requires an argument");
const char* filename = argv[1];
int tarFd = adb_open(filename, O_RDONLY);
@@ -1246,7 +1238,9 @@
return -1;
}
- printf("Now unlock your device and confirm the restore operation.\n");
+ fprintf(stdout, "Now unlock your device and confirm the restore operation.\n");
+ fflush(stdout);
+
copy_to_file(tarFd, fd);
// Provide an in-band EOD marker in case the archive file is malformed
@@ -1338,7 +1332,7 @@
} else if (!strcmp(*arg, "--")) {
ignore_flags = true;
} else {
- fprintf(stderr, "adb: unrecognized option '%s'\n", *arg);
+ syntax_error("unrecognized option '%s'", *arg);
exit(1);
}
}
@@ -1437,7 +1431,7 @@
/* this is a special flag used only when the ADB client launches the ADB Server */
is_daemon = 1;
} else if (!strcmp(argv[0], "--reply-fd")) {
- if (argc < 2) return usage("--reply-fd requires an argument");
+ if (argc < 2) return syntax_error("--reply-fd requires an argument");
const char* reply_fd_str = argv[1];
argc--;
argv++;
@@ -1449,7 +1443,7 @@
} else if (!strncmp(argv[0], "-p", 2)) {
const char* product = nullptr;
if (argv[0][2] == '\0') {
- if (argc < 2) return usage("-p requires an argument");
+ if (argc < 2) return syntax_error("-p requires an argument");
product = argv[1];
argc--;
argv++;
@@ -1465,7 +1459,7 @@
if (isdigit(argv[0][2])) {
serial = argv[0] + 2;
} else {
- if (argc < 2 || argv[0][2] != '\0') return usage("-s requires an argument");
+ if (argc < 2 || argv[0][2] != '\0') return syntax_error("-s requires an argument");
serial = argv[1];
argc--;
argv++;
@@ -1478,7 +1472,7 @@
gListenAll = 1;
} else if (!strncmp(argv[0], "-H", 2)) {
if (argv[0][2] == '\0') {
- if (argc < 2) return usage("-H requires an argument");
+ if (argc < 2) return syntax_error("-H requires an argument");
server_host_str = argv[1];
argc--;
argv++;
@@ -1487,7 +1481,7 @@
}
} else if (!strncmp(argv[0], "-P", 2)) {
if (argv[0][2] == '\0') {
- if (argc < 2) return usage("-P requires an argument");
+ if (argc < 2) return syntax_error("-P requires an argument");
server_port_str = argv[1];
argc--;
argv++;
@@ -1495,7 +1489,7 @@
server_port_str = argv[0] + 2;
}
} else if (!strcmp(argv[0], "-L")) {
- if (argc < 2) return usage("-L requires an argument");
+ if (argc < 2) return syntax_error("-L requires an argument");
server_socket_str = argv[1];
argc--;
argv++;
@@ -1508,8 +1502,7 @@
}
if ((server_host_str || server_port_str) && server_socket_str) {
- fprintf(stderr, "adb: -L is incompatible with -H or -P\n");
- exit(1);
+ return syntax_error("-L is incompatible with -H or -P");
}
// If -L, -H, or -P are specified, ignore environment variables.
@@ -1604,8 +1597,7 @@
} else if (argc == 2 && !strcmp(argv[1], "-l")) {
listopt = argv[1];
} else {
- fprintf(stderr, "Usage: adb devices [-l]\n");
- return 1;
+ return syntax_error("adb devices [-l]");
}
std::string query = android::base::StringPrintf("host:%s%s", argv[0], listopt);
@@ -1613,19 +1605,13 @@
return adb_query_command(query);
}
else if (!strcmp(argv[0], "connect")) {
- if (argc != 2) {
- fprintf(stderr, "Usage: adb connect <host>[:<port>]\n");
- return 1;
- }
+ if (argc != 2) return syntax_error("adb connect <host>[:<port>]");
std::string query = android::base::StringPrintf("host:connect:%s", argv[1]);
return adb_query_command(query);
}
else if (!strcmp(argv[0], "disconnect")) {
- if (argc > 2) {
- fprintf(stderr, "Usage: adb disconnect [<host>[:<port>]]\n");
- return 1;
- }
+ if (argc > 2) return syntax_error("adb disconnect [<host>[:<port>]]");
std::string query = android::base::StringPrintf("host:disconnect:%s",
(argc == 2) ? argv[1] : "");
@@ -1640,10 +1626,7 @@
else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
int exec_in = !strcmp(argv[0], "exec-in");
- if (argc < 2) {
- fprintf(stderr, "Usage: adb %s command\n", argv[0]);
- return 1;
- }
+ if (argc < 2) return syntax_error("adb %s command", argv[0]);
std::string cmd = "exec:";
cmd += argv[1];
@@ -1691,7 +1674,7 @@
}
}
else if (!strcmp(argv[0], "sideload")) {
- if (argc != 2) return usage("sideload requires an argument");
+ if (argc != 2) return syntax_error("sideload requires an argument");
if (adb_sideload_host(argv[1])) {
return 1;
} else {
@@ -1725,7 +1708,7 @@
bool reverse = !strcmp(argv[0], "reverse");
++argv;
--argc;
- if (argc < 1) return usage("%s requires an argument", argv[0]);
+ if (argc < 1) return syntax_error("%s requires an argument", argv[0]);
// Determine the <host-prefix> for this command.
std::string host_prefix;
@@ -1745,24 +1728,24 @@
std::string cmd, error;
if (strcmp(argv[0], "--list") == 0) {
- if (argc != 1) return usage("--list doesn't take any arguments");
+ if (argc != 1) return syntax_error("--list doesn't take any arguments");
return adb_query_command(host_prefix + ":list-forward");
} else if (strcmp(argv[0], "--remove-all") == 0) {
- if (argc != 1) return usage("--remove-all doesn't take any arguments");
+ if (argc != 1) return syntax_error("--remove-all doesn't take any arguments");
cmd = host_prefix + ":killforward-all";
} else if (strcmp(argv[0], "--remove") == 0) {
// forward --remove <local>
- if (argc != 2) return usage("--remove requires an argument");
+ if (argc != 2) return syntax_error("--remove requires an argument");
cmd = host_prefix + ":killforward:" + argv[1];
} else if (strcmp(argv[0], "--no-rebind") == 0) {
// forward --no-rebind <local> <remote>
- if (argc != 3) return usage("--no-rebind takes two arguments");
+ if (argc != 3) return syntax_error("--no-rebind takes two arguments");
if (forward_targets_are_valid(argv[1], argv[2], &error)) {
cmd = host_prefix + ":forward:norebind:" + argv[1] + ";" + argv[2];
}
} else {
// forward <local> <remote>
- if (argc != 2) return usage("forward takes two arguments");
+ if (argc != 2) return syntax_error("forward takes two arguments");
if (forward_targets_are_valid(argv[0], argv[1], &error)) {
cmd = host_prefix + ":forward:" + argv[0] + ";" + argv[1];
}
@@ -1791,7 +1774,7 @@
}
/* do_sync_*() commands */
else if (!strcmp(argv[0], "ls")) {
- if (argc != 2) return usage("ls requires an argument");
+ if (argc != 2) return syntax_error("ls requires an argument");
return do_sync_ls(argv[1]) ? 0 : 1;
}
else if (!strcmp(argv[0], "push")) {
@@ -1800,7 +1783,7 @@
const char* dst = nullptr;
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs);
- if (srcs.empty() || !dst) return usage("push requires an argument");
+ if (srcs.empty() || !dst) return syntax_error("push requires an argument");
return do_sync_push(srcs, dst) ? 0 : 1;
}
else if (!strcmp(argv[0], "pull")) {
@@ -1809,22 +1792,22 @@
const char* dst = ".";
parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, ©_attrs);
- if (srcs.empty()) return usage("pull requires an argument");
+ if (srcs.empty()) return syntax_error("pull requires an argument");
return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
}
else if (!strcmp(argv[0], "install")) {
- if (argc < 2) return usage("install requires an argument");
+ if (argc < 2) return syntax_error("install requires an argument");
if (_use_legacy_install()) {
return install_app_legacy(transport_type, serial, argc, argv);
}
return install_app(transport_type, serial, argc, argv);
}
else if (!strcmp(argv[0], "install-multiple")) {
- if (argc < 2) return usage("install-multiple requires an argument");
+ if (argc < 2) return syntax_error("install-multiple requires an argument");
return install_multiple_app(transport_type, serial, argc, argv);
}
else if (!strcmp(argv[0], "uninstall")) {
- if (argc < 2) return usage("uninstall requires an argument");
+ if (argc < 2) return syntax_error("uninstall requires an argument");
if (_use_legacy_install()) {
return uninstall_app_legacy(transport_type, serial, argc, argv);
}
@@ -1847,12 +1830,12 @@
// A local path or "android"/"data" arg was specified.
src = argv[1];
} else {
- return usage("usage: adb sync [-l] [PARTITION]");
+ return syntax_error("adb sync [-l] [PARTITION]");
}
if (src != "" &&
src != "system" && src != "data" && src != "vendor" && src != "oem") {
- return usage("don't know how to sync %s partition", src.c_str());
+ return syntax_error("don't know how to sync %s partition", src.c_str());
}
std::string system_src_path = product_file("system");
@@ -1904,7 +1887,7 @@
return restore(argc, argv);
}
else if (!strcmp(argv[0], "keygen")) {
- if (argc != 2) return usage("keygen requires an argument");
+ if (argc != 2) return syntax_error("keygen requires an argument");
// Always print key generation information for keygen command.
adb_trace_enable(AUTH);
return adb_auth_keygen(argv[1]);
@@ -1947,7 +1930,7 @@
return adb_query_command("host:host-features");
} else if (!strcmp(argv[0], "reconnect")) {
if (argc == 1) {
- return adb_query_command("host:reconnect");
+ return adb_query_command(format_host_command(argv[0], transport_type, serial));
} else if (argc == 2) {
if (!strcmp(argv[1], "device")) {
std::string err;
@@ -1957,12 +1940,12 @@
std::string err;
return adb_query_command("host:reconnect-offline");
} else {
- return usage("usage: adb reconnect [device|offline]");
+ return syntax_error("adb reconnect [device|offline]");
}
}
}
- usage("unknown command %s", argv[0]);
+ syntax_error("unknown command %s", argv[0]);
return 1;
}
@@ -1989,19 +1972,18 @@
// The last argument must be the APK file
const char* file = argv[argc - 1];
if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
- fprintf(stderr, "Filename doesn't end .apk: %s\n", file);
- return EXIT_FAILURE;
+ return syntax_error("filename doesn't end .apk: %s", file);
}
struct stat sb;
if (stat(file, &sb) == -1) {
- fprintf(stderr, "Failed to stat %s: %s\n", file, strerror(errno));
+ fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
return 1;
}
int localFd = adb_open(file, O_RDONLY);
if (localFd < 0) {
- fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+ fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
return 1;
}
@@ -2019,7 +2001,7 @@
int remoteFd = adb_connect(cmd, &error);
if (remoteFd < 0) {
- fprintf(stderr, "Connect error for write: %s\n", error.c_str());
+ fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
adb_close(localFd);
return 1;
}
@@ -2031,12 +2013,12 @@
adb_close(localFd);
adb_close(remoteFd);
- if (strncmp("Success", buf, 7)) {
- fprintf(stderr, "Failed to install %s: %s", file, buf);
- return 1;
+ if (!strncmp("Success", buf, 7)) {
+ fputs(buf, stdout);
+ return 0;
}
- fputs(buf, stderr);
- return 0;
+ fprintf(stderr, "adb: failed to install %s: %s", file, buf);
+ return 1;
}
static int install_multiple_app(TransportType transport, const char* serial, int argc,
@@ -2058,10 +2040,7 @@
}
}
- if (first_apk == -1) {
- fprintf(stderr, "No APK file on command line\n");
- return 1;
- }
+ if (first_apk == -1) return syntax_error("need APK file on command line");
std::string install_cmd;
if (_use_legacy_install()) {
@@ -2079,7 +2058,7 @@
std::string error;
int fd = adb_connect(cmd, &error);
if (fd < 0) {
- fprintf(stderr, "Connect error for create: %s\n", error.c_str());
+ fprintf(stderr, "adb: connect error for create: %s\n", error.c_str());
return EXIT_FAILURE;
}
char buf[BUFSIZ];
@@ -2096,7 +2075,7 @@
}
}
if (session_id < 0) {
- fprintf(stderr, "Failed to create session\n");
+ fprintf(stderr, "adb: failed to create session\n");
fputs(buf, stderr);
return EXIT_FAILURE;
}
@@ -2107,7 +2086,7 @@
const char* file = argv[i];
struct stat sb;
if (stat(file, &sb) == -1) {
- fprintf(stderr, "Failed to stat %s: %s\n", file, strerror(errno));
+ fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
success = 0;
goto finalize_session;
}
@@ -2119,7 +2098,7 @@
int localFd = adb_open(file, O_RDONLY);
if (localFd < 0) {
- fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+ fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
success = 0;
goto finalize_session;
}
@@ -2127,7 +2106,7 @@
std::string error;
int remoteFd = adb_connect(cmd, &error);
if (remoteFd < 0) {
- fprintf(stderr, "Connect error for write: %s\n", error.c_str());
+ fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
adb_close(localFd);
success = 0;
goto finalize_session;
@@ -2140,7 +2119,7 @@
adb_close(remoteFd);
if (strncmp("Success", buf, 7)) {
- fprintf(stderr, "Failed to write %s\n", file);
+ fprintf(stderr, "adb: failed to write %s\n", file);
fputs(buf, stderr);
success = 0;
goto finalize_session;
@@ -2154,20 +2133,19 @@
install_cmd.c_str(), success ? "commit" : "abandon", session_id);
fd = adb_connect(service, &error);
if (fd < 0) {
- fprintf(stderr, "Connect error for finalize: %s\n", error.c_str());
+ fprintf(stderr, "adb: connect error for finalize: %s\n", error.c_str());
return EXIT_FAILURE;
}
read_status_line(fd, buf, sizeof(buf));
adb_close(fd);
if (!strncmp("Success", buf, 7)) {
- fputs(buf, stderr);
+ fputs(buf, stdout);
return 0;
- } else {
- fprintf(stderr, "Failed to finalize session\n");
- fputs(buf, stderr);
- return EXIT_FAILURE;
}
+ fprintf(stderr, "adb: failed to finalize session\n");
+ fputs(buf, stderr);
+ return EXIT_FAILURE;
}
static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
@@ -2225,10 +2203,7 @@
}
}
- if (last_apk == -1) {
- fprintf(stderr, "No APK file on command line\n");
- return EXIT_FAILURE;
- }
+ if (last_apk == -1) return syntax_error("need APK file on command line");
int result = -1;
std::vector<const char*> apk_file = {argv[last_apk]};
diff --git a/adb/commandline.h b/adb/commandline.h
index 0cf655c..9ba69a3 100644
--- a/adb/commandline.h
+++ b/adb/commandline.h
@@ -87,7 +87,6 @@
extern DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK;
int adb_commandline(int argc, const char** argv);
-int usage();
// Connects to the device "shell" service with |command| and prints the
// resulting output.
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 04cd865..72c9eef 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -75,13 +75,13 @@
static bool main_thread_valid;
static unsigned long main_thread_id;
-static void check_main_thread() {
+void check_main_thread() {
if (main_thread_valid) {
CHECK_EQ(main_thread_id, adb_thread_id());
}
}
-static void set_main_thread() {
+void set_main_thread() {
main_thread_valid = true;
main_thread_id = adb_thread_id();
}
diff --git a/adb/fdevent.h b/adb/fdevent.h
index 207f9b7..e32845a 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -76,9 +76,12 @@
*/
void fdevent_loop();
+void check_main_thread();
+
// The following functions are used only for tests.
void fdevent_terminate_loop();
size_t fdevent_installed_count();
void fdevent_reset();
+void set_main_thread();
#endif
diff --git a/adb/services.cpp b/adb/services.cpp
index 4327044..9605e6e 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -347,7 +347,7 @@
std::string error = "unknown error";
const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : NULL;
atransport* t = acquire_one_transport(sinfo->transport_type, serial, &is_ambiguous, &error);
- if (t != nullptr && (sinfo->state == kCsAny || sinfo->state == t->connection_state)) {
+ if (t != nullptr && (sinfo->state == kCsAny || sinfo->state == t->GetConnectionState())) {
SendOkay(fd);
break;
} else if (!is_ambiguous) {
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 59a48f5..14ad1ff 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -794,7 +794,7 @@
if (!s->transport) {
SendFail(s->peer->fd, "device offline (no transport)");
goto fail;
- } else if (s->transport->connection_state == kCsOffline) {
+ } else if (s->transport->GetConnectionState() == kCsOffline) {
/* if there's no remote we fail the connection
** right here and terminate it
*/
diff --git a/adb/test_device.py b/adb/test_device.py
index e76aaed..a30972e 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -1188,6 +1188,77 @@
self.device.shell(['rm', '-f', '/data/local/tmp/adb-test-*'])
+class DeviceOfflineTest(DeviceTest):
+ def _get_device_state(self, serialno):
+ output = subprocess.check_output(self.device.adb_cmd + ['devices'])
+ for line in output.split('\n'):
+ m = re.match('(\S+)\s+(\S+)', line)
+ if m and m.group(1) == serialno:
+ return m.group(2)
+ return None
+
+ def test_killed_when_pushing_a_large_file(self):
+ """
+ While running adb push with a large file, kill adb server.
+ Occasionally the device becomes offline. Because the device is still
+ reading data without realizing that the adb server has been restarted.
+ Test if we can bring the device online automatically now.
+ http://b/32952319
+ """
+ serialno = subprocess.check_output(self.device.adb_cmd + ['get-serialno']).strip()
+ # 1. Push a large file
+ file_path = 'tmp_large_file'
+ try:
+ fh = open(file_path, 'w')
+ fh.write('\0' * (100 * 1024 * 1024))
+ fh.close()
+ subproc = subprocess.Popen(self.device.adb_cmd + ['push', file_path, '/data/local/tmp'])
+ time.sleep(0.1)
+ # 2. Kill the adb server
+ subprocess.check_call(self.device.adb_cmd + ['kill-server'])
+ subproc.terminate()
+ finally:
+ try:
+ os.unlink(file_path)
+ except:
+ pass
+ # 3. See if the device still exist.
+ # Sleep to wait for the adb server exit.
+ time.sleep(0.5)
+ # 4. The device should be online
+ self.assertEqual(self._get_device_state(serialno), 'device')
+
+ def test_killed_when_pulling_a_large_file(self):
+ """
+ While running adb pull with a large file, kill adb server.
+ Occasionally the device can't be connected. Because the device is trying to
+ send a message larger than what is expected by the adb server.
+ Test if we can bring the device online automatically now.
+ """
+ serialno = subprocess.check_output(self.device.adb_cmd + ['get-serialno']).strip()
+ file_path = 'tmp_large_file'
+ try:
+ # 1. Create a large file on device.
+ self.device.shell(['dd', 'if=/dev/zero', 'of=/data/local/tmp/tmp_large_file',
+ 'bs=1000000', 'count=100'])
+ # 2. Pull the large file on host.
+ subproc = subprocess.Popen(self.device.adb_cmd +
+ ['pull','/data/local/tmp/tmp_large_file', file_path])
+ time.sleep(0.1)
+ # 3. Kill the adb server
+ subprocess.check_call(self.device.adb_cmd + ['kill-server'])
+ subproc.terminate()
+ finally:
+ try:
+ os.unlink(file_path)
+ except:
+ pass
+ # 4. See if the device still exist.
+ # Sleep to wait for the adb server exit.
+ time.sleep(0.5)
+ self.assertEqual(self._get_device_state(serialno), 'device')
+
+
def main():
random.seed(0)
if len(adb.get_devices()) > 0:
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 4686841..cc8c162 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -33,6 +33,7 @@
#include <android-base/logging.h>
#include <android-base/parsenetaddress.h>
+#include <android-base/quick_exit.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -41,6 +42,7 @@
#include "adb_trace.h"
#include "adb_utils.h"
#include "diagnose_usb.h"
+#include "fdevent.h"
static void transport_unref(atransport *t);
@@ -209,6 +211,11 @@
put_apacket(p);
break;
}
+#if ADB_HOST
+ if (p->msg.command == 0) {
+ continue;
+ }
+#endif
}
D("%s: received remote packet, sending to transport", t->serial);
@@ -271,7 +278,11 @@
if (active) {
D("%s: transport got packet, sending to remote", t->serial);
ATRACE_NAME("write_transport write_remote");
- t->write_to_remote(p, t);
+ if (t->Write(p) != 0) {
+ D("%s: remote write failed for transport", t->serial);
+ put_apacket(p);
+ break;
+ }
} else {
D("%s: transport ignoring packet while offline", t->serial);
}
@@ -493,7 +504,7 @@
}
/* don't create transport threads for inaccessible devices */
- if (t->connection_state != kCsNoPerm) {
+ if (t->GetConnectionState() != kCsNoPerm) {
/* initial references are the two threads */
t->ref_count = 2;
@@ -538,6 +549,15 @@
transport_registration_func, 0);
fdevent_set(&transport_registration_fde, FDE_READ);
+#if ADB_HOST
+ android::base::at_quick_exit([]() {
+ // To avoid only writing part of a packet to a transport after exit, kick all transports.
+ std::lock_guard<std::mutex> lock(transport_lock);
+ for (auto t : transport_list) {
+ t->Kick();
+ }
+ });
+#endif
}
/* the fdevent select pump is single threaded */
@@ -600,7 +620,7 @@
}
atransport* acquire_one_transport(TransportType type, const char* serial, bool* is_ambiguous,
- std::string* error_out) {
+ std::string* error_out, bool accept_any_state) {
atransport* result = nullptr;
if (serial) {
@@ -615,7 +635,7 @@
std::unique_lock<std::mutex> lock(transport_lock);
for (const auto& t : transport_list) {
- if (t->connection_state == kCsNoPerm) {
+ if (t->GetConnectionState() == kCsNoPerm) {
#if ADB_HOST
*error_out = UsbNoPermissionsLongHelpText();
#endif
@@ -664,7 +684,7 @@
lock.unlock();
// Don't return unauthorized devices; the caller can't do anything with them.
- if (result && result->connection_state == kCsUnauthorized) {
+ if (result && result->GetConnectionState() == kCsUnauthorized && !accept_any_state) {
*error_out = "device unauthorized.\n";
char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
*error_out += "This adb server's $ADB_VENDOR_KEYS is ";
@@ -676,7 +696,7 @@
}
// Don't return offline devices; the caller can't do anything with them.
- if (result && result->connection_state == kCsOffline) {
+ if (result && result->GetConnectionState() == kCsOffline && !accept_any_state) {
*error_out = "device offline";
result = nullptr;
}
@@ -688,16 +708,38 @@
return result;
}
+int atransport::Write(apacket* p) {
+#if ADB_HOST
+ std::lock_guard<std::mutex> lock(write_msg_lock_);
+#endif
+ return write_func_(p, this);
+}
+
void atransport::Kick() {
if (!kicked_) {
kicked_ = true;
CHECK(kick_func_ != nullptr);
+#if ADB_HOST
+ // On host, adb server should avoid writing part of a packet, so don't
+ // kick a transport whiling writing a packet.
+ std::lock_guard<std::mutex> lock(write_msg_lock_);
+#endif
kick_func_(this);
}
}
+ConnectionState atransport::GetConnectionState() const {
+ return connection_state_;
+}
+
+void atransport::SetConnectionState(ConnectionState state) {
+ check_main_thread();
+ connection_state_ = state;
+}
+
const std::string atransport::connection_state_name() const {
- switch (connection_state) {
+ ConnectionState state = GetConnectionState();
+ switch (state) {
case kCsOffline:
return "offline";
case kCsBootloader:
@@ -963,10 +1005,10 @@
void register_usb_transport(usb_handle* usb, const char* serial, const char* devpath,
unsigned writeable) {
- atransport* t = new atransport();
+ atransport* t = new atransport((writeable ? kCsOffline : kCsNoPerm));
D("transport: %p init'ing for usb_handle %p (sn='%s')", t, usb, serial ? serial : "");
- init_usb_transport(t, usb, (writeable ? kCsOffline : kCsNoPerm));
+ init_usb_transport(t, usb);
if (serial) {
t->serial = strdup(serial);
}
@@ -987,12 +1029,13 @@
void unregister_usb_transport(usb_handle* usb) {
std::lock_guard<std::mutex> lock(transport_lock);
transport_list.remove_if(
- [usb](atransport* t) { return t->usb == usb && t->connection_state == kCsNoPerm; });
+ [usb](atransport* t) { return t->usb == usb && t->GetConnectionState() == kCsNoPerm; });
}
int check_header(apacket* p, atransport* t) {
if (p->msg.magic != (p->msg.command ^ 0xffffffff)) {
- VLOG(RWX) << "check_header(): invalid magic";
+ VLOG(RWX) << "check_header(): invalid magic command = " << std::hex << p->msg.command
+ << ", magic = " << p->msg.magic;
return -1;
}
@@ -1020,4 +1063,11 @@
keys_.pop_front();
return result;
}
+bool atransport::SetSendConnectOnError() {
+ if (has_send_connect_on_error_) {
+ return false;
+ }
+ has_send_connect_on_error_ = true;
+ return true;
+}
#endif
diff --git a/adb/transport.h b/adb/transport.h
index 4d97fc7..8c15d66 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -19,10 +19,12 @@
#include <sys/types.h>
+#include <atomic>
#include <deque>
#include <functional>
#include <list>
#include <memory>
+#include <mutex>
#include <string>
#include <unordered_set>
@@ -57,31 +59,35 @@
// class in one go is a very large change. Given how bad our testing is,
// it's better to do this piece by piece.
- atransport() {
+ atransport(ConnectionState state = kCsOffline) : connection_state_(state) {
transport_fde = {};
protocol_version = A_VERSION;
max_payload = MAX_PAYLOAD;
}
-
virtual ~atransport() {}
int (*read_from_remote)(apacket* p, atransport* t) = nullptr;
- int (*write_to_remote)(apacket* p, atransport* t) = nullptr;
void (*close)(atransport* t) = nullptr;
+
+ void SetWriteFunction(int (*write_func)(apacket*, atransport*)) { write_func_ = write_func; }
void SetKickFunction(void (*kick_func)(atransport*)) {
kick_func_ = kick_func;
}
bool IsKicked() {
return kicked_;
}
+ int Write(apacket* p);
void Kick();
+ // ConnectionState can be read by all threads, but can only be written in the main thread.
+ ConnectionState GetConnectionState() const;
+ void SetConnectionState(ConnectionState state);
+
int fd = -1;
int transport_socket = -1;
fdevent transport_fde;
size_t ref_count = 0;
uint32_t sync_token = 0;
- ConnectionState connection_state = kCsOffline;
bool online = false;
TransportType type = kTransportAny;
@@ -114,11 +120,13 @@
#if ADB_HOST
std::shared_ptr<RSA> NextKey();
+ bool SetSendConnectOnError();
#endif
char token[TOKEN_SIZE] = {};
size_t failed_auth_attempts = 0;
+ const std::string serial_name() const { return serial ? serial : "<unknown>"; }
const std::string connection_state_name() const;
void update_version(int version, size_t payload);
@@ -157,6 +165,7 @@
int local_port_for_emulator_ = -1;
bool kicked_ = false;
void (*kick_func_)(atransport*) = nullptr;
+ int (*write_func_)(apacket*, atransport*) = nullptr;
// A set of features transmitted in the banner with the initial connection.
// This is stored in the banner as 'features=feature0,feature1,etc'.
@@ -167,8 +176,11 @@
// A list of adisconnect callbacks called when the transport is kicked.
std::list<adisconnect*> disconnects_;
+ std::atomic<ConnectionState> connection_state_;
#if ADB_HOST
std::deque<std::shared_ptr<RSA>> keys_;
+ std::mutex write_msg_lock_;
+ bool has_send_connect_on_error_ = false;
#endif
DISALLOW_COPY_AND_ASSIGN(atransport);
@@ -181,8 +193,8 @@
* is set to true and nullptr returned.
* If no suitable transport is found, error is set and nullptr returned.
*/
-atransport* acquire_one_transport(TransportType type, const char* serial,
- bool* is_ambiguous, std::string* error_out);
+atransport* acquire_one_transport(TransportType type, const char* serial, bool* is_ambiguous,
+ std::string* error_out, bool accept_any_state = false);
void kick_transport(atransport* t);
void update_transports(void);
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 408f51f..3ee286a 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -515,12 +515,11 @@
int fail = 0;
t->SetKickFunction(remote_kick);
+ t->SetWriteFunction(remote_write);
t->close = remote_close;
t->read_from_remote = remote_read;
- t->write_to_remote = remote_write;
t->sfd = s;
t->sync_token = 1;
- t->connection_state = kCsOffline;
t->type = kTransportLocal;
#if ADB_HOST
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index 8b38e03..68689d4 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -94,12 +94,13 @@
}
TEST(transport, parse_banner_no_features) {
+ set_main_thread();
atransport t;
parse_banner("host::", &t);
ASSERT_EQ(0U, t.features().size());
- ASSERT_EQ(kCsHost, t.connection_state);
+ ASSERT_EQ(kCsHost, t.GetConnectionState());
ASSERT_EQ(nullptr, t.product);
ASSERT_EQ(nullptr, t.model);
@@ -113,7 +114,7 @@
"host::ro.product.name=foo;ro.product.model=bar;ro.product.device=baz;";
parse_banner(banner, &t);
- ASSERT_EQ(kCsHost, t.connection_state);
+ ASSERT_EQ(kCsHost, t.GetConnectionState());
ASSERT_EQ(0U, t.features().size());
@@ -130,7 +131,7 @@
"features=woodly,doodly";
parse_banner(banner, &t);
- ASSERT_EQ(kCsHost, t.connection_state);
+ ASSERT_EQ(kCsHost, t.GetConnectionState());
ASSERT_EQ(2U, t.features().size());
ASSERT_TRUE(t.has_feature("woodly"));
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 516b4f2..ce419b8 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -25,9 +25,115 @@
#include "adb.h"
+#if ADB_HOST
+
+static constexpr size_t MAX_USB_BULK_PACKET_SIZE = 1024u;
+
+// Call usb_read using a buffer having a multiple of MAX_USB_BULK_PACKET_SIZE bytes
+// to avoid overflow. See http://libusb.sourceforge.net/api-1.0/packetoverflow.html.
+static int UsbReadMessage(usb_handle* h, amessage* msg) {
+ D("UsbReadMessage");
+ char buffer[MAX_USB_BULK_PACKET_SIZE];
+ int n = usb_read(h, buffer, sizeof(buffer));
+ if (n == sizeof(*msg)) {
+ memcpy(msg, buffer, sizeof(*msg));
+ }
+ return n;
+}
+
+// Call usb_read using a buffer having a multiple of MAX_USB_BULK_PACKET_SIZE bytes
+// to avoid overflow. See http://libusb.sourceforge.net/api-1.0/packetoverflow.html.
+static int UsbReadPayload(usb_handle* h, apacket* p) {
+ D("UsbReadPayload");
+ size_t need_size = p->msg.data_length;
+ size_t data_pos = 0u;
+ while (need_size > 0u) {
+ int n = 0;
+ if (data_pos + MAX_USB_BULK_PACKET_SIZE <= sizeof(p->data)) {
+ // Read directly to p->data.
+ size_t rem_size = need_size % MAX_USB_BULK_PACKET_SIZE;
+ size_t direct_read_size = need_size - rem_size;
+ if (rem_size &&
+ data_pos + direct_read_size + MAX_USB_BULK_PACKET_SIZE <= sizeof(p->data)) {
+ direct_read_size += MAX_USB_BULK_PACKET_SIZE;
+ }
+ n = usb_read(h, &p->data[data_pos], direct_read_size);
+ if (n < 0) {
+ D("usb_read(size %zu) failed", direct_read_size);
+ return n;
+ }
+ } else {
+ // Read indirectly using a buffer.
+ char buffer[MAX_USB_BULK_PACKET_SIZE];
+ n = usb_read(h, buffer, sizeof(buffer));
+ if (n < 0) {
+ D("usb_read(size %zu) failed", sizeof(buffer));
+ return -1;
+ }
+ size_t copy_size = std::min(static_cast<size_t>(n), need_size);
+ D("usb read %d bytes, need %zu bytes, copy %zu bytes", n, need_size, copy_size);
+ memcpy(&p->data[data_pos], buffer, copy_size);
+ }
+ data_pos += n;
+ need_size -= std::min(static_cast<size_t>(n), need_size);
+ }
+ return static_cast<int>(data_pos);
+}
+
+static int remote_read(apacket* p, atransport* t) {
+ int n = UsbReadMessage(t->usb, &p->msg);
+ if (n < 0) {
+ D("remote usb: read terminated (message)");
+ return -1;
+ }
+ if (static_cast<size_t>(n) != sizeof(p->msg) || check_header(p, t)) {
+ D("remote usb: check_header failed, skip it");
+ goto err_msg;
+ }
+ if (t->GetConnectionState() == kCsOffline) {
+ // If we read a wrong msg header declaring a large message payload, don't read its payload.
+ // Otherwise we may miss true messages from the device.
+ if (p->msg.command != A_CNXN && p->msg.command != A_AUTH) {
+ goto err_msg;
+ }
+ }
+ if (p->msg.data_length) {
+ n = UsbReadPayload(t->usb, p);
+ if (n < 0) {
+ D("remote usb: terminated (data)");
+ return -1;
+ }
+ if (static_cast<uint32_t>(n) != p->msg.data_length) {
+ D("remote usb: read payload failed (need %u bytes, give %d bytes), skip it",
+ p->msg.data_length, n);
+ goto err_msg;
+ }
+ }
+ if (check_data(p)) {
+ D("remote usb: check_data failed, skip it");
+ goto err_msg;
+ }
+ return 0;
+
+err_msg:
+ p->msg.command = 0;
+ if (t->GetConnectionState() == kCsOffline) {
+ // If the data toggle of ep_out on device and ep_in on host are not the same, we may receive
+ // an error message. In this case, resend one A_CNXN message to connect the device.
+ if (t->SetSendConnectOnError()) {
+ SendConnectOnHost(t);
+ }
+ }
+ return 0;
+}
+
+#else
+
+// On Android devices, we rely on the kernel to provide buffered read.
+// So we can recover automatically from EOVERFLOW.
static int remote_read(apacket *p, atransport *t)
{
- if(usb_read(t->usb, &p->msg, sizeof(amessage))){
+ if (usb_read(t->usb, &p->msg, sizeof(amessage))) {
D("remote usb: read terminated (message)");
return -1;
}
@@ -38,7 +144,7 @@
}
if(p->msg.data_length) {
- if(usb_read(t->usb, p->data, p->msg.data_length)){
+ if (usb_read(t->usb, p->data, p->msg.data_length)) {
D("remote usb: terminated (data)");
return -1;
}
@@ -51,17 +157,18 @@
return 0;
}
+#endif
static int remote_write(apacket *p, atransport *t)
{
unsigned size = p->msg.data_length;
- if(usb_write(t->usb, &p->msg, sizeof(amessage))) {
+ if (usb_write(t->usb, &p->msg, sizeof(amessage))) {
D("remote usb: 1 - write terminated");
return -1;
}
if(p->msg.data_length == 0) return 0;
- if(usb_write(t->usb, &p->data, size)) {
+ if (usb_write(t->usb, &p->data, size)) {
D("remote usb: 2 - write terminated");
return -1;
}
@@ -75,20 +182,17 @@
t->usb = 0;
}
-static void remote_kick(atransport *t)
-{
+static void remote_kick(atransport* t) {
usb_kick(t->usb);
}
-void init_usb_transport(atransport *t, usb_handle *h, ConnectionState state)
-{
+void init_usb_transport(atransport* t, usb_handle* h) {
D("transport: usb");
t->close = remote_close;
t->SetKickFunction(remote_kick);
+ t->SetWriteFunction(remote_write);
t->read_from_remote = remote_read;
- t->write_to_remote = remote_write;
t->sync_token = 1;
- t->connection_state = state;
t->type = kTransportUsb;
t->usb = h;
}
diff --git a/base/Android.bp b/base/Android.bp
index 3af7686..81b96db 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -20,8 +20,25 @@
"-Werror",
]
+cc_library_headers {
+ name: "libbase_headers",
+ vendor_available: true,
+ host_supported: true,
+ export_include_dirs: ["include"],
+
+ target: {
+ linux_bionic: {
+ enabled: true,
+ },
+ windows: {
+ enabled: true,
+ },
+ },
+}
+
cc_library {
name: "libbase",
+ vendor_available: true,
clang: true,
host_supported: true,
srcs: [
@@ -33,9 +50,14 @@
"strings.cpp",
"test_utils.cpp",
],
- local_include_dirs: ["include"],
+
+ header_libs: [
+ "libbase_headers",
+ "libutils_headers",
+ ],
+ export_header_lib_headers: ["libbase_headers"],
+
cppflags: libbase_cppflags,
- export_include_dirs: ["include"],
shared_libs: ["liblog"],
target: {
android: {
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index cffa6ce..ab5beed 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -55,8 +55,6 @@
if (android::base::ReadFileToString(file_name, out_val)) {
return true;
}
-
- LINFO << "Error finding '" << key << "' in device tree";
}
return false;
diff --git a/init/Android.mk b/init/Android.mk
index f2c0842..de3d076 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -87,12 +87,12 @@
bootchart.cpp \
builtins.cpp \
init.cpp \
+ init_first_stage.cpp \
keychords.cpp \
property_service.cpp \
reboot.cpp \
signal_handler.cpp \
ueventd.cpp \
- ueventd_parser.cpp \
watchdogd.cpp \
LOCAL_MODULE:= init
@@ -143,6 +143,7 @@
LOCAL_SRC_FILES := \
devices_test.cpp \
init_parser_test.cpp \
+ init_test.cpp \
property_service_test.cpp \
util_test.cpp \
@@ -154,7 +155,7 @@
LOCAL_STATIC_LIBRARIES := libinit
LOCAL_SANITIZE := integer
LOCAL_CLANG := true
-LOCAL_CPPFLAGS := -Wall -Wextra -Werror
+LOCAL_CPPFLAGS := -Wall -Wextra -Werror -std=gnu++1z
include $(BUILD_NATIVE_TEST)
diff --git a/init/README.md b/init/README.md
index 1837868..9fc8d47 100644
--- a/init/README.md
+++ b/init/README.md
@@ -16,9 +16,7 @@
or options belong to the section most recently declared. Commands
or options before the first section are ignored.
-Actions and Services have unique names. If a second Action is defined
-with the same name as an existing one, its commands are appended to
-the commands of the existing action. If a second Service is defined
+Services have unique names. If a second Service is defined
with the same name as an existing one, it is ignored and an error
message is logged.
@@ -31,13 +29,21 @@
/init.rc is the primary .rc file and is loaded by the init executable
at the beginning of its execution. It is responsible for the initial
-set up of the system. It imports /init.${ro.hardware}.rc which is the
-primary vendor supplied .rc file.
+set up of the system.
-During the mount\_all command, the init executable loads all of the
-files contained within the /{system,vendor,odm}/etc/init/ directories.
-These directories are intended for all Actions and Services used after
-file system mounting.
+Devices that mount /system, /vendor through the first stage mount mechanism
+load all of the files contained within the
+/{system,vendor,odm}/etc/init/ directories immediately after loading
+the primary /init.rc. This is explained in more details in the
+Imports section of this file.
+
+Legacy devices without the first stage mount mechanism do the following:
+1. /init.rc imports /init.${ro.hardware}.rc which is the primary
+ vendor supplied .rc file.
+2. During the mount\_all command, the init executable loads all of the
+ files contained within the /{system,vendor,odm}/etc/init/ directories.
+ These directories are intended for all Actions and Services used after
+ file system mounting.
One may specify paths in the mount\_all command line to have it import
.rc files at the specified paths instead of the default ones listed above.
@@ -89,7 +95,7 @@
Actions
-------
Actions are named sequences of commands. Actions have a trigger which
-is used to determine when the action should occur. When an event
+is used to determine when the action is executed. When an event
occurs which matches an action's trigger, that action is added to
the tail of a to-be-executed queue (unless it is already on the
queue).
@@ -106,6 +112,34 @@
<command>
<command>
+Actions are added to the queue and executed based on the order that
+the file that contains them was parsed (see the Imports section), then
+sequentially within an individual file.
+
+For example if a file contains:
+
+ on boot
+ setprop a 1
+ setprop b 2
+
+ on boot && property:true=true
+ setprop c 1
+ setprop d 2
+
+ on boot
+ setprop e 1
+ setprop f 2
+
+Then when the `boot` trigger occurs and assuming the property `true`
+equals `true`, then the order of the commands executed will be:
+
+ setprop a 1
+ setprop b 2
+ setprop c 1
+ setprop d 2
+ setprop e 1
+ setprop f 2
+
Services
--------
@@ -458,21 +492,54 @@
Imports
-------
-The import keyword is not a command, but rather its own section and is
-handled immediately after the .rc file that contains it has finished
-being parsed. It takes the below form:
-
`import <path>`
> Parse an init config file, extending the current configuration.
If _path_ is a directory, each file in the directory is parsed as
a config file. It is not recursive, nested directories will
not be parsed.
-There are only two times where the init executable imports .rc files:
+The import keyword is not a command, but rather its own section,
+meaning that it does not happen as part of an Action, but rather,
+imports are handled as a file is being parsed and follow the below logic.
- 1. When it imports /init.rc during initial boot
- 2. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
- paths during mount_all
+There are only three times where the init executable imports .rc files:
+
+ 1. When it imports /init.rc or the script indicated by the property
+ `ro.boot.init_rc` during initial boot.
+ 2. When it imports /{system,vendor,odm}/etc/init/ for first stage mount
+ devices immediately after importing /init.rc.
+ 3. When it imports /{system,vendor,odm}/etc/init/ or .rc files at specified
+ paths during mount_all.
+
+The order that files are imported is a bit complex for legacy reasons
+and to keep backwards compatibility. It is not strictly guaranteed.
+
+The only correct way to guarantee that a command has been run before a
+different command is to either 1) place it in an Action with an
+earlier executed trigger, or 2) place it in an Action with the same
+trigger within the same file at an earlier line.
+
+Nonetheless, the defacto order for first stage mount devices is:
+1. /init.rc is parsed then recursively each of its imports are
+ parsed.
+2. The contents of /system/etc/init/ are alphabetized and parsed
+ sequentially, with imports happening recursively after each file is
+ parsed.
+3. Step 2 is repeated for /vendor/etc/init then /odm/etc/init
+
+The below pseudocode may explain this more clearly:
+
+ fn Import(file)
+ Parse(file)
+ for (import : file.imports)
+ Import(import)
+
+ Import(/init.rc)
+ Directories = [/system/etc/init, /vendor/etc/init, /odm/etc/init]
+ for (directory : Directories)
+ files = <Alphabetical order of directory's contents>
+ for (file : files)
+ Import(file)
Properties
diff --git a/init/action.cpp b/init/action.cpp
index 347edb0..21abe02 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -26,10 +26,8 @@
using android::base::Join;
using android::base::StringPrintf;
-Command::Command(BuiltinFunction f, const std::vector<std::string>& args,
- const std::string& filename, int line)
- : func_(f), args_(args), filename_(filename), line_(line) {
-}
+Command::Command(BuiltinFunction f, const std::vector<std::string>& args, int line)
+ : func_(f), args_(args), line_(line) {}
int Command::InvokeFunc() const {
std::vector<std::string> expanded_args;
@@ -49,50 +47,28 @@
return Join(args_, ' ');
}
-std::string Command::BuildSourceString() const {
- if (!filename_.empty()) {
- return StringPrintf(" (%s:%d)", filename_.c_str(), line_);
- } else {
- return std::string();
- }
-}
-
-Action::Action(bool oneshot) : oneshot_(oneshot) {
-}
+Action::Action(bool oneshot, const std::string& filename, int line)
+ : oneshot_(oneshot), filename_(filename), line_(line) {}
const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
-bool Action::AddCommand(const std::vector<std::string>& args,
- const std::string& filename, int line, std::string* err) {
+bool Action::AddCommand(const std::vector<std::string>& args, int line, std::string* err) {
if (!function_map_) {
*err = "no function map available";
return false;
}
- if (args.empty()) {
- *err = "command needed, but not provided";
- return false;
- }
-
- auto function = function_map_->FindFunction(args[0], args.size() - 1, err);
+ auto function = function_map_->FindFunction(args, err);
if (!function) {
return false;
}
- AddCommand(function, args, filename, line);
+ AddCommand(function, args, line);
return true;
}
-void Action::AddCommand(BuiltinFunction f,
- const std::vector<std::string>& args,
- const std::string& filename, int line) {
- commands_.emplace_back(f, args, filename, line);
-}
-
-void Action::CombineAction(const Action& action) {
- for (const auto& c : action.commands_) {
- commands_.emplace_back(c);
- }
+void Action::AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line) {
+ commands_.emplace_back(f, args, line);
}
std::size_t Action::NumCommands() const {
@@ -122,7 +98,7 @@
android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
std::string trigger_name = BuildTriggersString();
std::string cmd_str = command.BuildCommandString();
- std::string source = command.BuildSourceString();
+ std::string source = StringPrintf(" (%s:%d)", filename_.c_str(), command.line());
LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << source
<< " returned " << result << " took " << duration_ms << "ms.";
@@ -223,20 +199,17 @@
return found;
}
-bool Action::CheckEventTrigger(const std::string& trigger) const {
- return !event_trigger_.empty() &&
- trigger == event_trigger_ &&
- CheckPropertyTriggers();
+bool Action::CheckEvent(const EventTrigger& event_trigger) const {
+ return event_trigger == event_trigger_ && CheckPropertyTriggers();
}
-bool Action::CheckPropertyTrigger(const std::string& name,
- const std::string& value) const {
+bool Action::CheckEvent(const PropertyChange& property_change) const {
+ const auto& [name, value] = property_change;
return event_trigger_.empty() && CheckPropertyTriggers(name, value);
}
-bool Action::TriggersEqual(const Action& other) const {
- return property_triggers_ == other.property_triggers_ &&
- event_trigger_ == other.event_trigger_;
+bool Action::CheckEvent(const BuiltinAction& builtin_action) const {
+ return this == builtin_action;
}
std::string Action::BuildTriggersString() const {
@@ -262,41 +235,6 @@
}
}
-class EventTrigger : public Trigger {
-public:
- explicit EventTrigger(const std::string& trigger) : trigger_(trigger) {
- }
- bool CheckTriggers(const Action& action) const override {
- return action.CheckEventTrigger(trigger_);
- }
-private:
- const std::string trigger_;
-};
-
-class PropertyTrigger : public Trigger {
-public:
- PropertyTrigger(const std::string& name, const std::string& value)
- : name_(name), value_(value) {
- }
- bool CheckTriggers(const Action& action) const override {
- return action.CheckPropertyTrigger(name_, value_);
- }
-private:
- const std::string name_;
- const std::string value_;
-};
-
-class BuiltinTrigger : public Trigger {
-public:
- explicit BuiltinTrigger(Action* action) : action_(action) {
- }
- bool CheckTriggers(const Action& action) const override {
- return action_ == &action;
- }
-private:
- const Action* action_;
-};
-
ActionManager::ActionManager() : current_command_(0) {
}
@@ -306,56 +244,45 @@
}
void ActionManager::AddAction(std::unique_ptr<Action> action) {
- auto old_action_it =
- std::find_if(actions_.begin(), actions_.end(),
- [&action] (std::unique_ptr<Action>& a) {
- return action->TriggersEqual(*a);
- });
-
- if (old_action_it != actions_.end()) {
- (*old_action_it)->CombineAction(*action);
- } else {
- actions_.emplace_back(std::move(action));
- }
+ actions_.emplace_back(std::move(action));
}
void ActionManager::QueueEventTrigger(const std::string& trigger) {
- trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
+ event_queue_.emplace(trigger);
}
-void ActionManager::QueuePropertyTrigger(const std::string& name,
- const std::string& value) {
- trigger_queue_.push(std::make_unique<PropertyTrigger>(name, value));
+void ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) {
+ event_queue_.emplace(std::make_pair(name, value));
}
-void ActionManager::QueueAllPropertyTriggers() {
- QueuePropertyTrigger("", "");
+void ActionManager::QueueAllPropertyActions() {
+ QueuePropertyChange("", "");
}
-void ActionManager::QueueBuiltinAction(BuiltinFunction func,
- const std::string& name) {
- auto action = std::make_unique<Action>(true);
+void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
+ auto action = std::make_unique<Action>(true, "<Builtin Action>", 0);
std::vector<std::string> name_vector{name};
if (!action->InitSingleTrigger(name)) {
return;
}
- action->AddCommand(func, name_vector);
+ action->AddCommand(func, name_vector, 0);
- trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));
+ event_queue_.emplace(action.get());
actions_.emplace_back(std::move(action));
}
void ActionManager::ExecuteOneCommand() {
- // Loop through the trigger queue until we have an action to execute
- while (current_executing_actions_.empty() && !trigger_queue_.empty()) {
+ // Loop through the event queue until we have an action to execute
+ while (current_executing_actions_.empty() && !event_queue_.empty()) {
for (const auto& action : actions_) {
- if (trigger_queue_.front()->CheckTriggers(*action)) {
+ if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
+ event_queue_.front())) {
current_executing_actions_.emplace(action.get());
}
}
- trigger_queue_.pop();
+ event_queue_.pop();
}
if (current_executing_actions_.empty()) {
@@ -366,7 +293,8 @@
if (current_command_ == 0) {
std::string trigger_name = action->BuildTriggersString();
- LOG(INFO) << "processing action (" << trigger_name << ")";
+ LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
+ << ":" << action->line() << ")";
}
action->ExecuteOneCommand(current_command_);
@@ -388,7 +316,7 @@
}
bool ActionManager::HasMoreCommands() const {
- return !current_executing_actions_.empty() || !trigger_queue_.empty();
+ return !current_executing_actions_.empty() || !event_queue_.empty();
}
void ActionManager::DumpState() const {
@@ -397,15 +325,15 @@
}
}
-bool ActionParser::ParseSection(const std::vector<std::string>& args,
- std::string* err) {
+bool ActionParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line, std::string* err) {
std::vector<std::string> triggers(args.begin() + 1, args.end());
if (triggers.size() < 1) {
*err = "actions must have a trigger";
return false;
}
- auto action = std::make_unique<Action>(false);
+ auto action = std::make_unique<Action>(false, filename, line);
if (!action->InitTriggers(triggers, err)) {
return false;
}
@@ -414,14 +342,12 @@
return true;
}
-bool ActionParser::ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const {
- return action_ ? action_->AddCommand(args, filename, line, err) : false;
+bool ActionParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+ return action_ ? action_->AddCommand(std::move(args), line, err) : false;
}
void ActionParser::EndSection() {
if (action_ && action_->NumCommands() > 0) {
- ActionManager::GetInstance().AddAction(std::move(action_));
+ action_manager_->AddAction(std::move(action_));
}
}
diff --git a/init/action.h b/init/action.h
index 0bae9f0..d006c50 100644
--- a/init/action.h
+++ b/init/action.h
@@ -20,6 +20,7 @@
#include <map>
#include <queue>
#include <string>
+#include <variant>
#include <vector>
#include "builtins.h"
@@ -27,44 +28,44 @@
#include "keyword_map.h"
class Command {
-public:
- Command(BuiltinFunction f, const std::vector<std::string>& args,
- const std::string& filename, int line);
+ public:
+ Command(BuiltinFunction f, const std::vector<std::string>& args, int line);
int InvokeFunc() const;
std::string BuildCommandString() const;
- std::string BuildSourceString() const;
-private:
+ int line() const { return line_; }
+
+ private:
BuiltinFunction func_;
std::vector<std::string> args_;
- std::string filename_;
int line_;
};
-class Action {
-public:
- explicit Action(bool oneshot = false);
+using EventTrigger = std::string;
+using PropertyChange = std::pair<std::string, std::string>;
+using BuiltinAction = class Action*;
- bool AddCommand(const std::vector<std::string>& args,
- const std::string& filename, int line, std::string* err);
- void AddCommand(BuiltinFunction f,
- const std::vector<std::string>& args,
- const std::string& filename = "", int line = 0);
- void CombineAction(const Action& action);
+class Action {
+ public:
+ explicit Action(bool oneshot, const std::string& filename, int line);
+
+ bool AddCommand(const std::vector<std::string>& args, int line, std::string* err);
+ void AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line);
bool InitTriggers(const std::vector<std::string>& args, std::string* err);
bool InitSingleTrigger(const std::string& trigger);
std::size_t NumCommands() const;
void ExecuteOneCommand(std::size_t command) const;
void ExecuteAllCommands() const;
- bool CheckEventTrigger(const std::string& trigger) const;
- bool CheckPropertyTrigger(const std::string& name,
- const std::string& value) const;
- bool TriggersEqual(const Action& other) const;
+ bool CheckEvent(const EventTrigger& event_trigger) const;
+ bool CheckEvent(const PropertyChange& property_change) const;
+ bool CheckEvent(const BuiltinAction& builtin_action) const;
std::string BuildTriggersString() const;
void DumpState() const;
bool oneshot() const { return oneshot_; }
+ const std::string& filename() const { return filename_; }
+ int line() const { return line_; }
static void set_function_map(const KeywordMap<BuiltinFunction>* function_map) {
function_map_ = function_map;
}
@@ -80,53 +81,48 @@
std::string event_trigger_;
std::vector<Command> commands_;
bool oneshot_;
+ std::string filename_;
+ int line_;
static const KeywordMap<BuiltinFunction>* function_map_;
};
-class Trigger {
-public:
- virtual ~Trigger() { }
- virtual bool CheckTriggers(const Action& action) const = 0;
-};
-
class ActionManager {
-public:
+ public:
static ActionManager& GetInstance();
+ // Exposed for testing
+ ActionManager();
+
void AddAction(std::unique_ptr<Action> action);
void QueueEventTrigger(const std::string& trigger);
- void QueuePropertyTrigger(const std::string& name, const std::string& value);
- void QueueAllPropertyTriggers();
+ void QueuePropertyChange(const std::string& name, const std::string& value);
+ void QueueAllPropertyActions();
void QueueBuiltinAction(BuiltinFunction func, const std::string& name);
void ExecuteOneCommand();
bool HasMoreCommands() const;
void DumpState() const;
-private:
- ActionManager();
-
+ private:
ActionManager(ActionManager const&) = delete;
void operator=(ActionManager const&) = delete;
std::vector<std::unique_ptr<Action>> actions_;
- std::queue<std::unique_ptr<Trigger>> trigger_queue_;
+ std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_;
std::queue<const Action*> current_executing_actions_;
std::size_t current_command_;
};
class ActionParser : public SectionParser {
-public:
- ActionParser() : action_(nullptr) {
- }
- bool ParseSection(const std::vector<std::string>& args,
+ public:
+ ActionParser(ActionManager* action_manager)
+ : action_manager_(action_manager), action_(nullptr) {}
+ bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
std::string* err) override;
- bool ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const override;
+ bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
void EndSection() override;
- void EndFile(const std::string&) override {
- }
-private:
+
+ private:
+ ActionManager* action_manager_;
std::unique_ptr<Action> action_;
};
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 43eb420..1d98ef1 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -390,7 +390,7 @@
// Turning this on and letting the INFO logging be discarded adds 0.2s to
// Nexus 9 boot time, so it's disabled by default.
- if (false) parser.DumpState();
+ if (false) DumpState();
}
/* mount_fstab
@@ -838,7 +838,7 @@
return e4crypt_do_init_user0();
}
-BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
+const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
static const Map builtin_functions = {
diff --git a/init/builtins.h b/init/builtins.h
index 53f4a71..e1f0567 100644
--- a/init/builtins.h
+++ b/init/builtins.h
@@ -17,19 +17,20 @@
#ifndef _INIT_BUILTINS_H
#define _INIT_BUILTINS_H
+#include <functional>
#include <map>
#include <string>
#include <vector>
#include "keyword_map.h"
-using BuiltinFunction = int (*) (const std::vector<std::string>& args);
+using BuiltinFunction = std::function<int(const std::vector<std::string>&)>;
class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
-public:
- BuiltinFunctionMap() {
- }
-private:
- Map& map() const override;
+ public:
+ BuiltinFunctionMap() {}
+
+ private:
+ const Map& map() const override;
};
#endif
diff --git a/init/devices.cpp b/init/devices.cpp
index 6e13863..07d28d0 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -20,8 +20,10 @@
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
+#include <grp.h>
#include <libgen.h>
#include <linux/netlink.h>
+#include <pwd.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
@@ -49,7 +51,8 @@
#include <selinux/label.h>
#include <selinux/selinux.h>
-#include "ueventd_parser.h"
+#include "keyword_map.h"
+#include "ueventd.h"
#include "util.h"
extern struct selabel_handle *sehandle;
@@ -103,6 +106,137 @@
std::vector<Permissions> dev_permissions;
std::vector<SysfsPermissions> sysfs_permissions;
+bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err, bool is_sysfs) {
+ if (is_sysfs && args.size() != 5) {
+ *err = "/sys/ lines must have 5 entries";
+ return false;
+ }
+
+ if (!is_sysfs && args.size() != 4) {
+ *err = "/dev/ lines must have 4 entries";
+ return false;
+ }
+
+ auto it = args.begin();
+ const std::string& name = *it++;
+
+ std::string sysfs_attribute;
+ if (is_sysfs) sysfs_attribute = *it++;
+
+ // args is now common to both sys and dev entries and contains: <perm> <uid> <gid>
+ std::string& perm_string = *it++;
+ char* end_pointer = 0;
+ mode_t perm = strtol(perm_string.c_str(), &end_pointer, 8);
+ if (end_pointer == nullptr || *end_pointer != '\0') {
+ *err = "invalid mode '" + perm_string + "'";
+ return false;
+ }
+
+ std::string& uid_string = *it++;
+ passwd* pwd = getpwnam(uid_string.c_str());
+ if (!pwd) {
+ *err = "invalid uid '" + uid_string + "'";
+ return false;
+ }
+ uid_t uid = pwd->pw_uid;
+
+ std::string& gid_string = *it++;
+ struct group* grp = getgrnam(gid_string.c_str());
+ if (!grp) {
+ *err = "invalid gid '" + gid_string + "'";
+ return false;
+ }
+ gid_t gid = grp->gr_gid;
+
+ if (is_sysfs) {
+ sysfs_permissions.emplace_back(name, sysfs_attribute, perm, uid, gid);
+ } else {
+ dev_permissions.emplace_back(name, perm, uid, gid);
+ }
+ return true;
+}
+
+// TODO: Move this to be a member variable of a future devices class.
+static std::vector<Subsystem> subsystems;
+
+std::string Subsystem::ParseDevPath(uevent* uevent) const {
+ std::string devname = devname_source_ == DevnameSource::DEVNAME_UEVENT_DEVNAME
+ ? uevent->device_name
+ : android::base::Basename(uevent->path);
+
+ return dir_name_ + "/" + devname;
+}
+
+bool SubsystemParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line, std::string* err) {
+ if (args.size() != 2) {
+ *err = "subsystems must have exactly one name";
+ return false;
+ }
+
+ if (std::find(subsystems.begin(), subsystems.end(), args[1]) != subsystems.end()) {
+ *err = "ignoring duplicate subsystem entry";
+ return false;
+ }
+
+ subsystem_.name_ = args[1];
+
+ return true;
+}
+
+bool SubsystemParser::ParseDevName(std::vector<std::string>&& args, std::string* err) {
+ if (args[1] == "uevent_devname") {
+ subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVNAME;
+ return true;
+ }
+ if (args[1] == "uevent_devpath") {
+ subsystem_.devname_source_ = Subsystem::DevnameSource::DEVNAME_UEVENT_DEVPATH;
+ return true;
+ }
+
+ *err = "invalid devname '" + args[1] + "'";
+ return false;
+}
+
+bool SubsystemParser::ParseDirName(std::vector<std::string>&& args, std::string* err) {
+ if (args[1].front() != '/') {
+ *err = "dirname '" + args[1] + " ' does not start with '/'";
+ return false;
+ }
+
+ subsystem_.dir_name_ = args[1];
+ return true;
+}
+
+bool SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+ using OptionParser =
+ bool (SubsystemParser::*)(std::vector<std::string> && args, std::string * err);
+ static class OptionParserMap : public KeywordMap<OptionParser> {
+ private:
+ const Map& map() const override {
+ // clang-format off
+ static const Map option_parsers = {
+ {"devname", {1, 1, &SubsystemParser::ParseDevName}},
+ {"dirname", {1, 1, &SubsystemParser::ParseDirName}},
+ };
+ // clang-format on
+ return option_parsers;
+ }
+ } parser_map;
+
+ auto parser = parser_map.FindFunction(args, err);
+
+ if (!parser) {
+ return false;
+ }
+
+ return (this->*parser)(std::move(args), err);
+}
+
+void SubsystemParser::EndSection() {
+ subsystems.emplace_back(std::move(subsystem_));
+}
+
static void fixup_sys_permissions(const std::string& upath, const std::string& subsystem) {
// upaths omit the "/sys" that paths in this list
// contain, so we prepend it...
@@ -483,32 +617,9 @@
// if it's not a /dev device, nothing to do
if (uevent->major < 0 || uevent->minor < 0) return;
- std::string name = android::base::Basename(uevent->path);
- ueventd_subsystem* subsystem = ueventd_subsystem_find_by_name(uevent->subsystem.c_str());
-
std::string devpath;
- if (subsystem) {
- std::string devname;
-
- switch (subsystem->devname_src) {
- case DEVNAME_UEVENT_DEVNAME:
- devname = uevent->device_name;
- break;
-
- case DEVNAME_UEVENT_DEVPATH:
- devname = name;
- break;
-
- default:
- LOG(ERROR) << uevent->subsystem << " subsystem's devpath option is not set; ignoring event";
- return;
- }
-
- // TODO: Remove std::string()
- devpath = std::string(subsystem->dirname) + "/" + devname;
- mkdir_recursive(android::base::Dirname(devpath), 0755);
- } else if (android::base::StartsWith(uevent->subsystem, "usb")) {
+ if (android::base::StartsWith(uevent->subsystem, "usb")) {
if (uevent->subsystem == "usb") {
if (!uevent->device_name.empty()) {
devpath = "/dev/" + uevent->device_name;
@@ -520,15 +631,19 @@
int device_id = uevent->minor % 128 + 1;
devpath = android::base::StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
}
- mkdir_recursive(android::base::Dirname(devpath), 0755);
} else {
// ignore other USB events
return;
}
+ } else if (auto subsystem = std::find(subsystems.begin(), subsystems.end(), uevent->subsystem);
+ subsystem != subsystems.end()) {
+ devpath = subsystem->ParseDevPath(uevent);
} else {
- devpath = "/dev/" + name;
+ devpath = "/dev/" + android::base::Basename(uevent->path);
}
+ mkdir_recursive(android::base::Dirname(devpath), 0755);
+
auto links = get_character_device_symlinks(uevent);
handle_device(uevent->action, devpath, 0, uevent->major, uevent->minor, links);
diff --git a/init/devices.h b/init/devices.h
index 2cbae66..647b4c4 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -24,6 +24,8 @@
#include <string>
#include <vector>
+#include "init_parser.h"
+
enum coldboot_action_t {
// coldboot continues without creating the device for the uevent
COLDBOOT_CONTINUE = 0,
@@ -83,9 +85,45 @@
const std::string attribute_;
};
-extern std::vector<Permissions> dev_permissions;
-extern std::vector<SysfsPermissions> sysfs_permissions;
+class Subsystem {
+ public:
+ friend class SubsystemParser;
+ Subsystem() {}
+
+ // Returns the full path for a uevent of a device that is a member of this subsystem,
+ // according to the rules parsed from ueventd.rc
+ std::string ParseDevPath(uevent* uevent) const;
+
+ bool operator==(const std::string& string_name) { return name_ == string_name; }
+
+ private:
+ enum class DevnameSource {
+ DEVNAME_UEVENT_DEVNAME,
+ DEVNAME_UEVENT_DEVPATH,
+ };
+
+ std::string name_;
+ std::string dir_name_ = "/dev";
+ DevnameSource devname_source_;
+};
+
+class SubsystemParser : public SectionParser {
+ public:
+ SubsystemParser() {}
+ bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
+ std::string* err) override;
+ bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
+ void EndSection() override;
+
+ private:
+ bool ParseDevName(std::vector<std::string>&& args, std::string* err);
+ bool ParseDirName(std::vector<std::string>&& args, std::string* err);
+
+ Subsystem subsystem_;
+};
+
+bool ParsePermissionsLine(std::vector<std::string>&& args, std::string* err, bool is_sysfs);
typedef std::function<coldboot_action_t(struct uevent* uevent)> coldboot_callback;
extern coldboot_action_t handle_device_fd(coldboot_callback fn = nullptr);
extern void device_init(const char* path = nullptr, coldboot_callback fn = nullptr);
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
index 8a2bcc2..99275e5 100644
--- a/init/import_parser.cpp
+++ b/init/import_parser.cpp
@@ -20,8 +20,8 @@
#include "util.h"
-bool ImportParser::ParseSection(const std::vector<std::string>& args,
- std::string* err) {
+bool ImportParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line, std::string* err) {
if (args.size() != 2) {
*err = "single argument needed for import\n";
return false;
@@ -35,16 +35,18 @@
}
LOG(INFO) << "Added '" << conf_file << "' to import list";
- imports_.emplace_back(std::move(conf_file));
+ if (filename_.empty()) filename_ = filename;
+ imports_.emplace_back(std::move(conf_file), line);
return true;
}
-void ImportParser::EndFile(const std::string& filename) {
+void ImportParser::EndFile() {
auto current_imports = std::move(imports_);
imports_.clear();
- for (const auto& s : current_imports) {
- if (!Parser::GetInstance().ParseConfig(s)) {
- PLOG(ERROR) << "could not import file '" << s << "' from '" << filename << "'";
+ for (const auto& [import, line_num] : current_imports) {
+ if (!parser_->ParseConfig(import)) {
+ PLOG(ERROR) << filename_ << ": " << line_num << ": Could not import file '" << import
+ << "'";
}
}
}
diff --git a/init/import_parser.h b/init/import_parser.h
index 0e91025..45cbfad 100644
--- a/init/import_parser.h
+++ b/init/import_parser.h
@@ -23,21 +23,18 @@
#include <vector>
class ImportParser : public SectionParser {
-public:
- ImportParser() {
- }
- bool ParseSection(const std::vector<std::string>& args,
+ public:
+ ImportParser(Parser* parser) : parser_(parser) {}
+ bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
std::string* err) override;
- bool ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const override {
- return true;
- }
- void EndSection() override {
- }
- void EndFile(const std::string& filename) override;
-private:
- std::vector<std::string> imports_;
+ void EndFile() override;
+
+ private:
+ Parser* parser_;
+ // Store filename for later error reporting.
+ std::string filename_;
+ // Vector of imports and their line numbers for later error reporting.
+ std::vector<std::pair<std::string, int>> imports_;
};
#endif
diff --git a/init/init.cpp b/init/init.cpp
index 9a4aa24..99ce5e6 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -54,15 +54,13 @@
#include <fstream>
#include <memory>
-#include <set>
#include <vector>
#include "action.h"
#include "bootchart.h"
#include "devices.h"
-#include "fs_mgr.h"
-#include "fs_mgr_avb.h"
#include "import_parser.h"
+#include "init_first_stage.h"
#include "init_parser.h"
#include "keychords.h"
#include "log.h"
@@ -96,6 +94,11 @@
static std::string wait_prop_name;
static std::string wait_prop_value;
+void DumpState() {
+ ServiceManager::GetInstance().DumpState();
+ ActionManager::GetInstance().DumpState();
+}
+
void register_epoll_handler(int fd, void (*fn)()) {
epoll_event ev;
ev.events = EPOLLIN;
@@ -161,8 +164,8 @@
// waiting on a property.
if (name == "sys.powerctl") HandlePowerctlMessage(value);
- if (property_triggers_enabled)
- ActionManager::GetInstance().QueuePropertyTrigger(name, value);
+ if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
+
if (waiting_for_prop) {
if (wait_prop_name == name && wait_prop_value == value) {
wait_prop_name.clear();
@@ -489,71 +492,10 @@
}
}
-/* Reads the content of device tree file into dt_value.
- * Returns true if the read is success, false otherwise.
- */
-static bool read_dt_file(const std::string& file_name, std::string* dt_value) {
- if (android::base::ReadFileToString(file_name, dt_value)) {
- if (!dt_value->empty()) {
- dt_value->pop_back(); // Trim the trailing '\0' out.
- return true;
- }
- }
- return false;
-}
-
-static const std::string kAndroidDtDir("/proc/device-tree/firmware/android/");
-
-static bool is_dt_value_expected(const std::string& dt_file_suffix,
- const std::string& expected_value) {
- std::string dt_value;
- std::string file_name = kAndroidDtDir + dt_file_suffix;
-
- if (read_dt_file(file_name, &dt_value)) {
- if (dt_value == expected_value) {
- return true;
- }
- }
- return false;
-}
-
-static inline bool is_dt_compatible() {
- return is_dt_value_expected("compatible", "android,firmware");
-}
-
-static inline bool is_dt_fstab_compatible() {
- return is_dt_value_expected("fstab/compatible", "android,fstab");
-}
-
-static inline bool is_dt_vbmeta_compatible() {
- return is_dt_value_expected("vbmeta/compatible", "android,vbmeta");
-}
-
-// Gets the vbmeta config from device tree. Specifically, the 'parts' and 'by_name_prefix'.
-// /{
-// firmware {
-// android {
-// vbmeta {
-// compatible = "android,vbmeta";
-// parts = "vbmeta,boot,system,vendor"
-// by_name_prefix="/dev/block/platform/soc.0/f9824900.sdhci/by-name/"
-// };
-// };
-// };
-// }
-static bool get_vbmeta_config_from_dt(std::string* vbmeta_partitions,
- std::string* device_file_by_name_prefix) {
- std::string file_name = kAndroidDtDir + "vbmeta/parts";
- if (!read_dt_file(file_name, vbmeta_partitions)) return false;
-
- file_name = kAndroidDtDir + "vbmeta/by_name_prefix";
- if (!read_dt_file(file_name, device_file_by_name_prefix)) return false;
-
- return true;
-}
-
static void process_kernel_dt() {
- if (!is_dt_compatible()) return;
+ if (!is_android_dt_value_expected("compatible", "android,firmware")) {
+ return;
+ }
std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(kAndroidDtDir.c_str()), closedir);
if (!dir) return;
@@ -593,7 +535,7 @@
static int queue_property_triggers_action(const std::vector<std::string>& args)
{
ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, "enable_property_trigger");
- ActionManager::GetInstance().QueueAllPropertyTriggers();
+ ActionManager::GetInstance().QueueAllPropertyActions();
return 0;
}
@@ -958,285 +900,6 @@
}
}
-// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
-static void device_init_dm_device(const std::string& dm_device) {
- const std::string device_name(basename(dm_device.c_str()));
- const std::string syspath = "/sys/block/" + device_name;
-
- device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
- if (uevent->device_name == device_name) {
- LOG(VERBOSE) << "early_mount: creating dm-verity device : " << dm_device;
- return COLDBOOT_STOP;
- }
- return COLDBOOT_CONTINUE;
- });
- device_close();
-}
-
-static bool vboot_1_0_mount_partitions(const std::vector<fstab_rec*>& fstab_recs) {
- if (fstab_recs.empty()) return false;
-
- for (auto rec : fstab_recs) {
- bool need_create_dm_device = false;
- if (fs_mgr_is_verified(rec)) {
- // setup verity and create the dm-XX block device
- // needed to mount this partition
- int ret = fs_mgr_setup_verity(rec, false /* wait_for_verity_dev */);
- if (ret == FS_MGR_SETUP_VERITY_DISABLED) {
- LOG(INFO) << "verity disabled for '" << rec->mount_point << "'";
- } else if (ret == FS_MGR_SETUP_VERITY_SUCCESS) {
- need_create_dm_device = true;
- } else {
- PLOG(ERROR) << "early_mount: failed to setup verity for '" << rec->mount_point
- << "'";
- return false;
- }
- }
- if (need_create_dm_device) {
- // The exact block device name (rec->blk_device) is changed to "/dev/block/dm-XX".
- // Need to create it because ueventd isn't started during early mount.
- device_init_dm_device(rec->blk_device);
- }
- if (fs_mgr_do_mount_one(rec)) {
- PLOG(ERROR) << "early_mount: failed to mount '" << rec->mount_point << "'";
- return false;
- }
- }
-
- return true;
-}
-
-static bool vboot_2_0_mount_partitions(const std::vector<fstab_rec*>& fstab_recs,
- const std::string& device_file_by_name_prefix) {
- if (fstab_recs.empty()) return false;
-
- FsManagerAvbUniquePtr avb_handle = FsManagerAvbHandle::Open(device_file_by_name_prefix);
- if (!avb_handle) {
- LOG(INFO) << "Failed to Open FsManagerAvbHandle";
- return false;
- }
-
- setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
- for (auto rec : fstab_recs) {
- bool need_create_dm_device = false;
- if (fs_mgr_is_avb(rec)) {
- if (avb_handle->hashtree_disabled()) {
- LOG(INFO) << "avb hashtree disabled for '" << rec->mount_point << "'";
- } else if (avb_handle->SetUpAvb(rec, false /* wait_for_verity_dev */)) {
- need_create_dm_device = true;
- } else {
- PLOG(ERROR) << "early_mount: failed to set up AVB on partition: '"
- << rec->mount_point << "'";
- return false;
- }
- }
- if (need_create_dm_device) {
- // The exact block device name (rec->blk_device) is changed to "/dev/block/dm-XX".
- // Need to create it because ueventd isn't started during early mount.
- device_init_dm_device(rec->blk_device);
- }
- if (fs_mgr_do_mount_one(rec)) {
- PLOG(ERROR) << "early_mount: failed to mount '" << rec->mount_point << "'";
- return false;
- }
- }
-
- return true;
-}
-
-static bool mount_early_partitions(const std::vector<fstab_rec*>& fstab_recs,
- const std::string& device_file_by_name_prefix) {
- if (is_dt_vbmeta_compatible()) { // AVB (external/avb) is used to setup dm-verity.
- return vboot_2_0_mount_partitions(fstab_recs, device_file_by_name_prefix);
- } else {
- return vboot_1_0_mount_partitions(fstab_recs);
- }
-}
-
-// Creates devices with uevent->partition_name matching one in the in/out
-// partition_names. Note that the partition_names MUST have A/B suffix
-// when A/B is used. Found partitions will then be removed from the
-// partition_names for caller to check which devices are NOT created.
-static void early_device_init(std::set<std::string>* partition_names) {
- if (partition_names->empty()) {
- return;
- }
- device_init(nullptr, [=](uevent* uevent) -> coldboot_action_t {
- // we need platform devices to create symlinks
- if (uevent->subsystem == "platform") {
- return COLDBOOT_CREATE;
- }
-
- // Ignore everything that is not a block device
- if (uevent->subsystem != "block") {
- return COLDBOOT_CONTINUE;
- }
-
- if (!uevent->partition_name.empty()) {
- // match partition names to create device nodes for partitions
- // both partition_names and uevent->partition_name have A/B suffix when A/B is used
- auto iter = partition_names->find(uevent->partition_name);
- if (iter != partition_names->end()) {
- LOG(VERBOSE) << "early_mount: found partition: " << *iter;
- partition_names->erase(iter);
- if (partition_names->empty()) {
- return COLDBOOT_STOP; // found all partitions, stop coldboot
- } else {
- return COLDBOOT_CREATE; // create this device and continue to find others
- }
- }
- }
- // Not found a partition or find an unneeded partition, continue to find others
- return COLDBOOT_CONTINUE;
- });
-}
-
-static bool vboot_1_0_early_partitions(const std::vector<fstab_rec*>& early_fstab_recs,
- std::set<std::string>* out_partitions,
- bool* out_need_verity) {
- std::string meta_partition;
- for (auto fstab_rec : early_fstab_recs) {
- // don't allow verifyatboot for early mounted partitions
- if (fs_mgr_is_verifyatboot(fstab_rec)) {
- LOG(ERROR) << "early_mount: partitions can't be verified at boot";
- return false;
- }
- // check for verified partitions
- if (fs_mgr_is_verified(fstab_rec)) {
- *out_need_verity = true;
- }
- // check if verity metadata is on a separate partition and get partition
- // name from the end of the ->verity_loc path. verity state is not partition
- // specific, so there must be only 1 additional partition that carries
- // verity state.
- if (fstab_rec->verity_loc) {
- if (!meta_partition.empty()) {
- LOG(ERROR) << "early_mount: more than one meta partition found: " << meta_partition
- << ", " << basename(fstab_rec->verity_loc);
- return false;
- } else {
- meta_partition = basename(fstab_rec->verity_loc);
- }
- }
- }
-
- // includes those early mount partitions and meta_partition (if any)
- // note that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used
- for (auto fstab_rec : early_fstab_recs) {
- out_partitions->emplace(basename(fstab_rec->blk_device));
- }
-
- if (!meta_partition.empty()) {
- out_partitions->emplace(std::move(meta_partition));
- }
-
- return true;
-}
-
-// a.k.a. AVB (external/avb)
-static bool vboot_2_0_early_partitions(std::set<std::string>* out_partitions, bool* out_need_verity,
- std::string* out_device_file_by_name_prefix) {
- std::string vbmeta_partitions;
- if (!get_vbmeta_config_from_dt(&vbmeta_partitions, out_device_file_by_name_prefix)) {
- return false;
- }
- // libavb verifies AVB metadata on all verified partitions at once.
- // e.g., The vbmeta_partitions will be "vbmeta,boot,system,vendor"
- // for libavb to verify metadata, even if we only need to early mount /vendor.
- std::vector<std::string> partitions = android::base::Split(vbmeta_partitions, ",");
- std::string ab_suffix = fs_mgr_get_slot_suffix();
- for (const auto& partition : partitions) {
- out_partitions->emplace(partition + ab_suffix);
- }
- *out_need_verity = true;
- return true;
-}
-
-static bool get_early_partitions(const std::vector<fstab_rec*>& early_fstab_recs,
- std::set<std::string>* out_partitions, bool* out_need_verity,
- std::string* out_device_file_by_name_prefix) {
- *out_need_verity = false;
- out_partitions->clear();
- out_device_file_by_name_prefix->clear();
-
- if (is_dt_vbmeta_compatible()) { // AVB (external/avb) is used to setup dm-verity.
- return vboot_2_0_early_partitions(out_partitions, out_need_verity,
- out_device_file_by_name_prefix);
- } else {
- return vboot_1_0_early_partitions(early_fstab_recs, out_partitions, out_need_verity);
- }
-}
-
-/* Early mount vendor and ODM partitions. The fstab is read from device-tree. */
-static bool early_mount() {
- // skip early mount if we're in recovery mode
- if (access("/sbin/recovery", F_OK) == 0) {
- LOG(INFO) << "Early mount skipped (recovery mode)";
- return true;
- }
-
- // first check if device tree fstab entries are compatible
- if (!is_dt_fstab_compatible()) {
- LOG(INFO) << "Early mount skipped (missing/incompatible fstab in device tree)";
- return true;
- }
-
- std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> tab(
- fs_mgr_read_fstab_dt(), fs_mgr_free_fstab);
- if (!tab) {
- LOG(ERROR) << "Early mount failed to read fstab from device tree";
- return false;
- }
-
- // find out fstab records for odm, system and vendor
- std::vector<fstab_rec*> early_fstab_recs;
- for (auto mount_point : {"/odm", "/system", "/vendor"}) {
- fstab_rec* fstab_rec = fs_mgr_get_entry_for_mount_point(tab.get(), mount_point);
- if (fstab_rec != nullptr) {
- early_fstab_recs.push_back(fstab_rec);
- }
- }
-
- // nothing to early mount
- if (early_fstab_recs.empty()) return true;
-
- bool need_verity;
- std::string device_file_by_name_prefix;
- std::set<std::string> partition_names;
- // partition_names MUST have A/B suffix when A/B is used
- if (!get_early_partitions(early_fstab_recs, &partition_names, &need_verity,
- &device_file_by_name_prefix)) {
- return false;
- }
-
- bool success = false;
- // create the devices we need..
- early_device_init(&partition_names);
-
- // early_device_init will remove found partitions from partition_names
- // So if the partition_names is not empty here, means some partitions
- // are not found
- if (!partition_names.empty()) {
- LOG(ERROR) << "early_mount: partition(s) not found: "
- << android::base::Join(partition_names, ", ");
- goto done;
- }
-
- if (need_verity) {
- // create /dev/device mapper
- device_init("/sys/devices/virtual/misc/device-mapper",
- [&](uevent* uevent) -> coldboot_action_t { return COLDBOOT_STOP; });
- }
-
- if (mount_early_partitions(early_fstab_recs, device_file_by_name_prefix)) {
- success = true;
- }
-
-done:
- device_close();
- return success;
-}
-
static void install_reboot_signal_handlers() {
// Instead of panic'ing the kernel as is the default behavior when init crashes,
// we prefer to reboot to bootloader on development builds, as this will prevent
@@ -1315,11 +978,13 @@
LOG(INFO) << "init first stage started!";
- if (!early_mount()) {
+ if (!DoFirstStageMount()) {
LOG(ERROR) << "Failed to mount required partitions early ...";
panic();
}
+ SetInitAvbVersionInRecovery();
+
// Set up SELinux, loading the SELinux policy.
selinux_initialize(true);
@@ -1398,10 +1063,13 @@
const BuiltinFunctionMap function_map;
Action::set_function_map(&function_map);
+ ActionManager& am = ActionManager::GetInstance();
+ ServiceManager& sm = ServiceManager::GetInstance();
Parser& parser = Parser::GetInstance();
- parser.AddSectionParser("service",std::make_unique<ServiceParser>());
- parser.AddSectionParser("on", std::make_unique<ActionParser>());
- parser.AddSectionParser("import", std::make_unique<ImportParser>());
+
+ parser.AddSectionParser("service", std::make_unique<ServiceParser>(&sm));
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
+ parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
parser.ParseConfig("/init.rc");
@@ -1419,9 +1087,7 @@
// Turning this on and letting the INFO logging be discarded adds 0.2s to
// Nexus 9 boot time, so it's disabled by default.
- if (false) parser.DumpState();
-
- ActionManager& am = ActionManager::GetInstance();
+ if (false) DumpState();
am.QueueEventTrigger("early-init");
@@ -1456,10 +1122,10 @@
// By default, sleep until something happens.
int epoll_timeout_ms = -1;
- if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
+ if (!(waiting_for_prop || sm.IsWaitingForExec())) {
am.ExecuteOneCommand();
}
- if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
+ if (!(waiting_for_prop || sm.IsWaitingForExec())) {
restart_processes();
// If there's a process that needs restarting, wake up in time for that.
diff --git a/init/init.h b/init/init.h
index 1da3350..6add75f 100644
--- a/init/init.h
+++ b/init/init.h
@@ -34,4 +34,6 @@
bool start_waiting_for_property(const char *name, const char *value);
+void DumpState();
+
#endif /* _INIT_INIT_H */
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
new file mode 100644
index 0000000..43f1c15
--- /dev/null
+++ b/init/init_first_stage.cpp
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "init_first_stage.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include "devices.h"
+#include "fs_mgr.h"
+#include "fs_mgr_avb.h"
+#include "util.h"
+
+// Class Declarations
+// ------------------
+class FirstStageMount {
+ public:
+ FirstStageMount();
+ virtual ~FirstStageMount() = default;
+
+ // The factory method to create either FirstStageMountVBootV1 or FirstStageMountVBootV2
+ // based on device tree configurations.
+ static std::unique_ptr<FirstStageMount> Create();
+ bool DoFirstStageMount(); // Mounts fstab entries read from device tree.
+ bool InitDevices();
+
+ protected:
+ void InitRequiredDevices(std::set<std::string>* devices_partition_names);
+ void InitVerityDevice(const std::string& verity_device);
+ bool MountPartitions();
+
+ virtual bool GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
+ bool* out_need_dm_verity) = 0;
+ virtual bool SetUpDmVerity(fstab_rec* fstab_rec) = 0;
+
+ // Device tree fstab entries.
+ std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> device_tree_fstab_;
+ // Eligible first stage mount candidates, only allow /system, /vendor and/or /odm.
+ std::vector<fstab_rec*> mount_fstab_recs_;
+};
+
+class FirstStageMountVBootV1 : public FirstStageMount {
+ public:
+ FirstStageMountVBootV1() = default;
+ ~FirstStageMountVBootV1() override = default;
+
+ protected:
+ bool GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
+ bool* out_need_dm_verity) override;
+ bool SetUpDmVerity(fstab_rec* fstab_rec) override;
+};
+
+class FirstStageMountVBootV2 : public FirstStageMount {
+ public:
+ FirstStageMountVBootV2();
+ ~FirstStageMountVBootV2() override = default;
+
+ const std::string& by_name_prefix() const { return device_tree_by_name_prefix_; }
+
+ protected:
+ bool GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
+ bool* out_need_dm_verity) override;
+ bool SetUpDmVerity(fstab_rec* fstab_rec) override;
+ bool InitAvbHandle();
+
+ std::string device_tree_vbmeta_parts_;
+ std::string device_tree_by_name_prefix_;
+ FsManagerAvbUniquePtr avb_handle_;
+};
+
+// Static Functions
+// ----------------
+static inline bool IsDtVbmetaCompatible() {
+ return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
+}
+
+static bool inline IsRecoveryMode() {
+ return access("/sbin/recovery", F_OK) == 0;
+}
+
+// Class Definitions
+// -----------------
+FirstStageMount::FirstStageMount() : device_tree_fstab_(fs_mgr_read_fstab_dt(), fs_mgr_free_fstab) {
+ if (!device_tree_fstab_) {
+ LOG(ERROR) << "Failed to read fstab from device tree";
+ return;
+ }
+ for (auto mount_point : {"/system", "/vendor", "/odm"}) {
+ fstab_rec* fstab_rec =
+ fs_mgr_get_entry_for_mount_point(device_tree_fstab_.get(), mount_point);
+ if (fstab_rec != nullptr) {
+ mount_fstab_recs_.push_back(fstab_rec);
+ }
+ }
+}
+
+std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
+ if (IsDtVbmetaCompatible()) {
+ return std::make_unique<FirstStageMountVBootV2>();
+ } else {
+ return std::make_unique<FirstStageMountVBootV1>();
+ }
+}
+
+bool FirstStageMount::DoFirstStageMount() {
+ // Nothing to mount.
+ if (mount_fstab_recs_.empty()) return true;
+
+ if (!InitDevices()) return false;
+
+ if (!MountPartitions()) return false;
+
+ return true;
+}
+
+bool FirstStageMount::InitDevices() {
+ bool need_dm_verity;
+ std::set<std::string> devices_partition_names;
+
+ // The partition name in devices_partition_names MUST have A/B suffix when A/B is used.
+ if (!GetRequiredDevices(&devices_partition_names, &need_dm_verity)) return false;
+
+ if (need_dm_verity) {
+ device_init("/sys/devices/virtual/misc/device-mapper",
+ [&](uevent* uevent) -> coldboot_action_t { return COLDBOOT_STOP; });
+ }
+
+ bool success = false;
+ InitRequiredDevices(&devices_partition_names);
+
+ // InitRequiredDevices() will remove found partitions from devices_partition_names.
+ // So if it isn't empty here, it means some partitions are not found.
+ if (!devices_partition_names.empty()) {
+ LOG(ERROR) << __FUNCTION__ << "(): partition(s) not found: "
+ << android::base::Join(devices_partition_names, ", ");
+ } else {
+ success = true;
+ }
+
+ device_close();
+ return success;
+}
+
+// Creates devices with uevent->partition_name matching one in the in/out
+// devices_partition_names. Found partitions will then be removed from the
+// devices_partition_names for the caller to check which devices are NOT created.
+void FirstStageMount::InitRequiredDevices(std::set<std::string>* devices_partition_names) {
+ if (devices_partition_names->empty()) {
+ return;
+ }
+ device_init(nullptr, [=](uevent* uevent) -> coldboot_action_t {
+ // We need platform devices to create symlinks.
+ if (uevent->subsystem == "platform") {
+ return COLDBOOT_CREATE;
+ }
+
+ // Ignores everything that is not a block device.
+ if (uevent->subsystem != "block") {
+ return COLDBOOT_CONTINUE;
+ }
+
+ if (!uevent->partition_name.empty()) {
+ // Matches partition name to create device nodes.
+ // Both devices_partition_names and uevent->partition_name have A/B
+ // suffix when A/B is used.
+ auto iter = devices_partition_names->find(uevent->partition_name);
+ if (iter != devices_partition_names->end()) {
+ LOG(VERBOSE) << __FUNCTION__ << "(): found partition: " << *iter;
+ devices_partition_names->erase(iter);
+ if (devices_partition_names->empty()) {
+ return COLDBOOT_STOP; // Found all partitions, stop coldboot.
+ } else {
+ return COLDBOOT_CREATE; // Creates this device and continue to find others.
+ }
+ }
+ }
+ // Not found a partition or find an unneeded partition, continue to find others.
+ return COLDBOOT_CONTINUE;
+ });
+}
+
+// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
+void FirstStageMount::InitVerityDevice(const std::string& verity_device) {
+ const std::string device_name(basename(verity_device.c_str()));
+ const std::string syspath = "/sys/block/" + device_name;
+
+ device_init(syspath.c_str(), [&](uevent* uevent) -> coldboot_action_t {
+ if (uevent->device_name == device_name) {
+ LOG(VERBOSE) << "Creating dm-verity device : " << verity_device;
+ return COLDBOOT_STOP;
+ }
+ return COLDBOOT_CONTINUE;
+ });
+ device_close();
+}
+
+bool FirstStageMount::MountPartitions() {
+ for (auto fstab_rec : mount_fstab_recs_) {
+ if (!SetUpDmVerity(fstab_rec)) {
+ PLOG(ERROR) << "Failed to setup verity for '" << fstab_rec->mount_point << "'";
+ return false;
+ }
+ if (fs_mgr_do_mount_one(fstab_rec)) {
+ PLOG(ERROR) << "Failed to mount '" << fstab_rec->mount_point << "'";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool FirstStageMountVBootV1::GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
+ bool* out_need_dm_verity) {
+ std::string verity_loc_device;
+ *out_need_dm_verity = false;
+
+ for (auto fstab_rec : mount_fstab_recs_) {
+ // Don't allow verifyatboot in the first stage.
+ if (fs_mgr_is_verifyatboot(fstab_rec)) {
+ LOG(ERROR) << "Partitions can't be verified at boot";
+ return false;
+ }
+ // Checks for verified partitions.
+ if (fs_mgr_is_verified(fstab_rec)) {
+ *out_need_dm_verity = true;
+ }
+ // Checks if verity metadata is on a separate partition. Note that it is
+ // not partition specific, so there must be only one additional partition
+ // that carries verity state.
+ if (fstab_rec->verity_loc) {
+ if (verity_loc_device.empty()) {
+ verity_loc_device = fstab_rec->verity_loc;
+ } else if (verity_loc_device != fstab_rec->verity_loc) {
+ LOG(ERROR) << "More than one verity_loc found: " << verity_loc_device << ", "
+ << fstab_rec->verity_loc;
+ return false;
+ }
+ }
+ }
+
+ // Includes the partition names of fstab records and verity_loc_device (if any).
+ // Notes that fstab_rec->blk_device has A/B suffix updated by fs_mgr when A/B is used.
+ for (auto fstab_rec : mount_fstab_recs_) {
+ out_devices_partition_names->emplace(basename(fstab_rec->blk_device));
+ }
+
+ if (!verity_loc_device.empty()) {
+ out_devices_partition_names->emplace(basename(verity_loc_device.c_str()));
+ }
+
+ return true;
+}
+
+bool FirstStageMountVBootV1::SetUpDmVerity(fstab_rec* fstab_rec) {
+ if (fs_mgr_is_verified(fstab_rec)) {
+ int ret = fs_mgr_setup_verity(fstab_rec, false /* wait_for_verity_dev */);
+ if (ret == FS_MGR_SETUP_VERITY_DISABLED) {
+ LOG(INFO) << "Verity disabled for '" << fstab_rec->mount_point << "'";
+ } else if (ret == FS_MGR_SETUP_VERITY_SUCCESS) {
+ // The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
+ // Needs to create it because ueventd isn't started in init first stage.
+ InitVerityDevice(fstab_rec->blk_device);
+ } else {
+ return false;
+ }
+ }
+ return true; // Returns true to mount the partition.
+}
+
+// FirstStageMountVBootV2 constructor.
+// Gets the vbmeta configurations from device tree.
+// Specifically, the 'parts' and 'by_name_prefix' below.
+// /{
+// firmware {
+// android {
+// vbmeta {
+// compatible = "android,vbmeta";
+// parts = "vbmeta,boot,system,vendor"
+// by_name_prefix = "/dev/block/platform/soc.0/f9824900.sdhci/by-name/"
+// };
+// };
+// };
+// }
+FirstStageMountVBootV2::FirstStageMountVBootV2() : avb_handle_(nullptr) {
+ if (!read_android_dt_file("vbmeta/parts", &device_tree_vbmeta_parts_)) {
+ PLOG(ERROR) << "Failed to read vbmeta/parts from device tree";
+ return;
+ }
+
+ // TODO: removes by_name_prefix to allow partitions on different block devices.
+ if (!read_android_dt_file("vbmeta/by_name_prefix", &device_tree_by_name_prefix_)) {
+ PLOG(ERROR) << "Failed to read vbmeta/by_name_prefix from dt";
+ return;
+ }
+}
+
+bool FirstStageMountVBootV2::GetRequiredDevices(std::set<std::string>* out_devices_partition_names,
+ bool* out_need_dm_verity) {
+ *out_need_dm_verity = false;
+
+ // fstab_rec->blk_device has A/B suffix.
+ for (auto fstab_rec : mount_fstab_recs_) {
+ if (fs_mgr_is_avb(fstab_rec)) {
+ *out_need_dm_verity = true;
+ }
+ out_devices_partition_names->emplace(basename(fstab_rec->blk_device));
+ }
+
+ // libavb verifies AVB metadata on all verified partitions at once.
+ // e.g., The device_tree_vbmeta_parts_ will be "vbmeta,boot,system,vendor"
+ // for libavb to verify metadata, even if there is only /vendor in the
+ // above mount_fstab_recs_.
+ if (*out_need_dm_verity) {
+ if (device_tree_vbmeta_parts_.empty()) {
+ LOG(ERROR) << "Missing vbmeta parts in device tree";
+ return false;
+ }
+ std::vector<std::string> partitions = android::base::Split(device_tree_vbmeta_parts_, ",");
+ std::string ab_suffix = fs_mgr_get_slot_suffix();
+ for (const auto& partition : partitions) {
+ // out_devices_partition_names is of type std::set so it's not an issue to emplace
+ // a partition twice. e.g., /vendor might be in both places:
+ // - device_tree_vbmeta_parts_ = "vbmeta,boot,system,vendor"
+ // - mount_fstab_recs_: /vendor_a
+ out_devices_partition_names->emplace(partition + ab_suffix);
+ }
+ }
+ return true;
+}
+
+bool FirstStageMountVBootV2::SetUpDmVerity(fstab_rec* fstab_rec) {
+ if (fs_mgr_is_avb(fstab_rec)) {
+ if (!InitAvbHandle()) return false;
+ if (avb_handle_->hashtree_disabled()) {
+ LOG(INFO) << "avb hashtree disabled for '" << fstab_rec->mount_point << "'";
+ } else if (avb_handle_->SetUpAvb(fstab_rec, false /* wait_for_verity_dev */)) {
+ // The exact block device name (fstab_rec->blk_device) is changed to "/dev/block/dm-XX".
+ // Needs to create it because ueventd isn't started in init first stage.
+ InitVerityDevice(fstab_rec->blk_device);
+ } else {
+ return false;
+ }
+ }
+ return true; // Returns true to mount the partition.
+}
+
+bool FirstStageMountVBootV2::InitAvbHandle() {
+ if (avb_handle_) return true; // Returns true if the handle is already initialized.
+
+ avb_handle_ = FsManagerAvbHandle::Open(device_tree_by_name_prefix_);
+ if (!avb_handle_) {
+ PLOG(ERROR) << "Failed to open FsManagerAvbHandle";
+ return false;
+ }
+ // Sets INIT_AVB_VERSION here for init to set ro.boot.avb_version in the second stage.
+ setenv("INIT_AVB_VERSION", avb_handle_->avb_version().c_str(), 1);
+ return true;
+}
+
+// Public functions
+// ----------------
+// Mounts /system, /vendor, and/or /odm if they are present in the fstab provided by device tree.
+bool DoFirstStageMount() {
+ // Skips first stage mount if we're in recovery mode.
+ if (IsRecoveryMode()) {
+ LOG(INFO) << "First stage mount skipped (recovery mode)";
+ return true;
+ }
+
+ // Firstly checks if device tree fstab entries are compatible.
+ if (!is_android_dt_value_expected("fstab/compatible", "android,fstab")) {
+ LOG(INFO) << "First stage mount skipped (missing/incompatible fstab in device tree)";
+ return true;
+ }
+
+ std::unique_ptr<FirstStageMount> handle = FirstStageMount::Create();
+ if (!handle) {
+ LOG(ERROR) << "Failed to create FirstStageMount";
+ return false;
+ }
+ return handle->DoFirstStageMount();
+}
+
+void SetInitAvbVersionInRecovery() {
+ if (!IsRecoveryMode()) {
+ LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not in recovery mode)";
+ return;
+ }
+
+ if (!IsDtVbmetaCompatible()) {
+ LOG(INFO) << "Skipped setting INIT_AVB_VERSION (not vbmeta compatible)";
+ return;
+ }
+
+ // Initializes required devices for the subsequent FsManagerAvbHandle::Open()
+ // to verify AVB metadata on all partitions in the verified chain.
+ // We only set INIT_AVB_VERSION when the AVB verification succeeds, i.e., the
+ // Open() function returns a valid handle.
+ // We don't need to mount partitions here in recovery mode.
+ FirstStageMountVBootV2 avb_first_mount;
+ if (!avb_first_mount.InitDevices()) {
+ LOG(ERROR) << "Failed to init devices for INIT_AVB_VERSION";
+ return;
+ }
+
+ FsManagerAvbUniquePtr avb_handle = FsManagerAvbHandle::Open(avb_first_mount.by_name_prefix());
+ if (!avb_handle) {
+ PLOG(ERROR) << "Failed to open FsManagerAvbHandle for INIT_AVB_VERSION";
+ return;
+ }
+ setenv("INIT_AVB_VERSION", avb_handle->avb_version().c_str(), 1);
+}
diff --git a/init/ueventd_parser.h b/init/init_first_stage.h
similarity index 60%
rename from init/ueventd_parser.h
rename to init/init_first_stage.h
index 4d69897..170a24c 100644
--- a/init/ueventd_parser.h
+++ b/init/init_first_stage.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,15 +14,10 @@
* limitations under the License.
*/
-#ifndef _INIT_UEVENTD_PARSER_H_
-#define _INIT_UEVENTD_PARSER_H_
+#ifndef _INIT_FIRST_STAGE_H
+#define _INIT_FIRST_STAGE_H
-#include "ueventd.h"
-
-#define UEVENTD_PARSER_MAXARGS 5
-
-int ueventd_parse_config_file(const char *fn);
-void set_device_permission(const char* fn, int line, int nargs, char **args);
-struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name);
+bool DoFirstStageMount();
+void SetInitAvbVersionInRecovery();
#endif
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 53e670b..620367a 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -17,14 +17,13 @@
#include "init_parser.h"
#include <dirent.h>
-#include <fcntl.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
-#include "action.h"
#include "parser.h"
-#include "service.h"
+#include "util.h"
Parser::Parser() {
}
@@ -39,13 +38,16 @@
section_parsers_[name] = std::move(parser);
}
+void Parser::AddSingleLineParser(const std::string& prefix, LineCallback callback) {
+ line_callbacks_.emplace_back(prefix, callback);
+}
+
void Parser::ParseData(const std::string& filename, const std::string& data) {
//TODO: Use a parser with const input and remove this copy
std::vector<char> data_copy(data.begin(), data.end());
data_copy.push_back('\0');
parse_state state;
- state.filename = filename.c_str();
state.line = 0;
state.ptr = &data_copy[0];
state.nexttoken = 0;
@@ -65,21 +67,34 @@
if (args.empty()) {
break;
}
+ // If we have a line matching a prefix we recognize, call its callback and unset any
+ // current section parsers. This is meant for /sys/ and /dev/ line entries for uevent.
+ for (const auto& [prefix, callback] : line_callbacks_) {
+ if (android::base::StartsWith(args[0], prefix.c_str())) {
+ if (section_parser) section_parser->EndSection();
+
+ std::string ret_err;
+ if (!callback(std::move(args), &ret_err)) {
+ LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
+ }
+ section_parser = nullptr;
+ break;
+ }
+ }
if (section_parsers_.count(args[0])) {
if (section_parser) {
section_parser->EndSection();
}
section_parser = section_parsers_[args[0]].get();
std::string ret_err;
- if (!section_parser->ParseSection(args, &ret_err)) {
- parse_error(&state, "%s\n", ret_err.c_str());
+ if (!section_parser->ParseSection(std::move(args), filename, state.line, &ret_err)) {
+ LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
section_parser = nullptr;
}
} else if (section_parser) {
std::string ret_err;
- if (!section_parser->ParseLineSection(args, state.filename,
- state.line, &ret_err)) {
- parse_error(&state, "%s\n", ret_err.c_str());
+ if (!section_parser->ParseLineSection(std::move(args), state.line, &ret_err)) {
+ LOG(ERROR) << filename << ": " << state.line << ": " << ret_err;
}
}
args.clear();
@@ -101,8 +116,8 @@
data.push_back('\n'); // TODO: fix parse_config.
ParseData(path, data);
- for (const auto& sp : section_parsers_) {
- sp.second->EndFile(path);
+ for (const auto& [section_name, section_parser] : section_parsers_) {
+ section_parser->EndFile();
}
LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
@@ -142,8 +157,3 @@
}
return ParseConfigFile(path);
}
-
-void Parser::DumpState() const {
- ServiceManager::GetInstance().DumpState();
- ActionManager::GetInstance().DumpState();
-}
diff --git a/init/init_parser.h b/init/init_parser.h
index 6935fdf..bd8a178 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -22,47 +22,72 @@
#include <string>
#include <vector>
+// SectionParser is an interface that can parse a given 'section' in init.
+//
+// You can implement up to 4 functions below, with ParseSection() being mandatory.
+// The first two function return bool with false indicating a failure and has a std::string* err
+// parameter into which an error string can be written. It will be reported along with the
+// filename and line number of where the error occurred.
+//
+// 1) bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
+// int line, std::string* err)
+// This function is called when a section is first encountered.
+//
+// 2) bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err)
+// This function is called on each subsequent line until the next section is encountered.
+//
+// 3) bool EndSection()
+// This function is called either when a new section is found or at the end of the file.
+// It indicates that parsing of the current section is complete and any relevant objects should
+// be committed.
+//
+// 4) bool EndFile()
+// This function is called at the end of the file.
+// It indicates that the parsing has completed and any relevant objects should be committed.
+
class SectionParser {
-public:
- virtual ~SectionParser() {
- }
- virtual bool ParseSection(const std::vector<std::string>& args,
- std::string* err) = 0;
- virtual bool ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const = 0;
- virtual void EndSection() = 0;
- virtual void EndFile(const std::string& filename) = 0;
+ public:
+ virtual ~SectionParser() {}
+ virtual bool ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line, std::string* err) = 0;
+ virtual bool ParseLineSection(std::vector<std::string>&&, int, std::string*) { return true; };
+ virtual void EndSection(){};
+ virtual void EndFile(){};
};
class Parser {
-public:
+ public:
+ // LineCallback is the type for callbacks that can parse a line starting with a given prefix.
+ //
+ // They take the form of bool Callback(std::vector<std::string>&& args, std::string* err)
+ //
+ // Similar to ParseSection() and ParseLineSection(), this function returns bool with false
+ // indicating a failure and has an std::string* err parameter into which an error string can
+ // be written.
+ using LineCallback = std::function<bool(std::vector<std::string>&&, std::string*)>;
+
static Parser& GetInstance();
- void DumpState() const;
+
+ // Exposed for testing
+ Parser();
+
bool ParseConfig(const std::string& path);
- void AddSectionParser(const std::string& name,
- std::unique_ptr<SectionParser> parser);
- void set_is_system_etc_init_loaded(bool loaded) {
- is_system_etc_init_loaded_ = loaded;
- }
- void set_is_vendor_etc_init_loaded(bool loaded) {
- is_vendor_etc_init_loaded_ = loaded;
- }
- void set_is_odm_etc_init_loaded(bool loaded) {
- is_odm_etc_init_loaded_ = loaded;
- }
+ void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);
+ void AddSingleLineParser(const std::string& prefix, LineCallback callback);
+ void set_is_system_etc_init_loaded(bool loaded) { is_system_etc_init_loaded_ = loaded; }
+ void set_is_vendor_etc_init_loaded(bool loaded) { is_vendor_etc_init_loaded_ = loaded; }
+ void set_is_odm_etc_init_loaded(bool loaded) { is_odm_etc_init_loaded_ = loaded; }
bool is_system_etc_init_loaded() { return is_system_etc_init_loaded_; }
bool is_vendor_etc_init_loaded() { return is_vendor_etc_init_loaded_; }
bool is_odm_etc_init_loaded() { return is_odm_etc_init_loaded_; }
-private:
- Parser();
-
+ private:
void ParseData(const std::string& filename, const std::string& data);
bool ParseConfigFile(const std::string& path);
bool ParseConfigDir(const std::string& path);
std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
+ std::vector<std::pair<std::string, LineCallback>> line_callbacks_;
bool is_system_etc_init_loaded_ = false;
bool is_vendor_etc_init_loaded_ = false;
bool is_odm_etc_init_loaded_ = false;
diff --git a/init/init_test.cpp b/init/init_test.cpp
new file mode 100644
index 0000000..3da14b5
--- /dev/null
+++ b/init/init_test.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <functional>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "action.h"
+#include "builtins.h"
+#include "import_parser.h"
+#include "init_parser.h"
+#include "keyword_map.h"
+#include "util.h"
+
+class TestFunctionMap : public KeywordMap<BuiltinFunction> {
+ public:
+ // Helper for argument-less functions
+ using BuiltinFunctionNoArgs = std::function<void(void)>;
+ void Add(const std::string& name, const BuiltinFunctionNoArgs function) {
+ Add(name, 0, 0, [function](const std::vector<std::string>&) {
+ function();
+ return 0;
+ });
+ }
+
+ void Add(const std::string& name, std::size_t min_parameters, std::size_t max_parameters,
+ const BuiltinFunction function) {
+ builtin_functions_[name] = make_tuple(min_parameters, max_parameters, function);
+ }
+
+ private:
+ Map builtin_functions_ = {};
+
+ const Map& map() const override { return builtin_functions_; }
+};
+
+using ActionManagerCommand = std::function<void(ActionManager&)>;
+
+void TestInit(const std::string& init_script_file, const TestFunctionMap& test_function_map,
+ const std::vector<ActionManagerCommand>& commands) {
+ ActionManager am;
+
+ Action::set_function_map(&test_function_map);
+
+ Parser parser;
+ parser.AddSectionParser("on", std::make_unique<ActionParser>(&am));
+ parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
+
+ ASSERT_TRUE(parser.ParseConfig(init_script_file));
+
+ for (const auto& command : commands) {
+ command(am);
+ }
+
+ while (am.HasMoreCommands()) {
+ am.ExecuteOneCommand();
+ }
+}
+
+void TestInitText(const std::string& init_script, const TestFunctionMap& test_function_map,
+ const std::vector<ActionManagerCommand>& commands) {
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));
+ TestInit(tf.path, test_function_map, commands);
+}
+
+TEST(init, SimpleEventTrigger) {
+ bool expect_true = false;
+ std::string init_script =
+ R"init(
+on boot
+pass_test
+)init";
+
+ TestFunctionMap test_function_map;
+ test_function_map.Add("pass_test", [&expect_true]() { expect_true = true; });
+
+ ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+ std::vector<ActionManagerCommand> commands{trigger_boot};
+
+ TestInitText(init_script, test_function_map, commands);
+
+ EXPECT_TRUE(expect_true);
+}
+
+TEST(init, EventTriggerOrder) {
+ std::string init_script =
+ R"init(
+on boot
+execute_first
+
+on boot && property:ro.hardware=*
+execute_second
+
+on boot
+execute_third
+
+)init";
+
+ int num_executed = 0;
+ TestFunctionMap test_function_map;
+ test_function_map.Add("execute_first", [&num_executed]() { EXPECT_EQ(0, num_executed++); });
+ test_function_map.Add("execute_second", [&num_executed]() { EXPECT_EQ(1, num_executed++); });
+ test_function_map.Add("execute_third", [&num_executed]() { EXPECT_EQ(2, num_executed++); });
+
+ ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+ std::vector<ActionManagerCommand> commands{trigger_boot};
+
+ TestInitText(init_script, test_function_map, commands);
+}
+
+TEST(init, EventTriggerOrderMultipleFiles) {
+ // 6 total files, which should have their triggers executed in the following order:
+ // 1: start - original script parsed
+ // 2: first_import - immediately imported by first_script
+ // 3: dir_a - file named 'a.rc' in dir; dir is imported after first_import
+ // 4: a_import - file imported by dir_a
+ // 5: dir_b - file named 'b.rc' in dir
+ // 6: last_import - imported after dir is imported
+
+ TemporaryFile first_import;
+ ASSERT_TRUE(first_import.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 2", first_import.fd));
+
+ TemporaryFile dir_a_import;
+ ASSERT_TRUE(dir_a_import.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 4", dir_a_import.fd));
+
+ TemporaryFile last_import;
+ ASSERT_TRUE(last_import.fd != -1);
+ ASSERT_TRUE(android::base::WriteStringToFd("on boot\nexecute 6", last_import.fd));
+
+ TemporaryDir dir;
+ // clang-format off
+ std::string dir_a_script = "import " + std::string(dir_a_import.path) + "\n"
+ "on boot\n"
+ "execute 3";
+ // clang-format on
+ // write_file() ensures the right mode is set
+ ASSERT_TRUE(write_file(std::string(dir.path) + "/a.rc", dir_a_script));
+
+ ASSERT_TRUE(write_file(std::string(dir.path) + "/b.rc", "on boot\nexecute 5"));
+
+ // clang-format off
+ std::string start_script = "import " + std::string(first_import.path) + "\n"
+ "import " + std::string(dir.path) + "\n"
+ "import " + std::string(last_import.path) + "\n"
+ "on boot\n"
+ "execute 1";
+ // clang-format on
+ TemporaryFile start;
+ ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));
+
+ int num_executed = 0;
+ auto execute_command = [&num_executed](const std::vector<std::string>& args) {
+ EXPECT_EQ(2U, args.size());
+ EXPECT_EQ(++num_executed, std::stoi(args[1]));
+ return 0;
+ };
+
+ TestFunctionMap test_function_map;
+ test_function_map.Add("execute", 1, 1, execute_command);
+
+ ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger("boot"); };
+ std::vector<ActionManagerCommand> commands{trigger_boot};
+
+ TestInit(start.path, test_function_map, commands);
+
+ EXPECT_EQ(6, num_executed);
+}
diff --git a/init/keyword_map.h b/init/keyword_map.h
index 693d82a..88bad01 100644
--- a/init/keyword_map.h
+++ b/init/keyword_map.h
@@ -24,18 +24,23 @@
template <typename Function>
class KeywordMap {
-public:
+ public:
using FunctionInfo = std::tuple<std::size_t, std::size_t, Function>;
- using Map = const std::map<std::string, FunctionInfo>;
+ using Map = std::map<std::string, FunctionInfo>;
virtual ~KeywordMap() {
}
- const Function FindFunction(const std::string& keyword,
- size_t num_args,
- std::string* err) const {
+ const Function FindFunction(const std::vector<std::string>& args, std::string* err) const {
using android::base::StringPrintf;
+ if (args.empty()) {
+ *err = "keyword needed, but not provided";
+ return nullptr;
+ }
+ auto& keyword = args[0];
+ auto num_args = args.size() - 1;
+
auto function_info_it = map().find(keyword);
if (function_info_it == map().end()) {
*err = StringPrintf("invalid keyword '%s'", keyword.c_str());
@@ -68,10 +73,10 @@
return std::get<Function>(function_info);
}
-private:
-//Map of keyword ->
-//(minimum number of arguments, maximum number of arguments, function pointer)
- virtual Map& map() const = 0;
+ private:
+ // Map of keyword ->
+ // (minimum number of arguments, maximum number of arguments, function pointer)
+ virtual const Map& map() const = 0;
};
#endif
diff --git a/init/parser.cpp b/init/parser.cpp
index 5953a88..0d13cfe 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -1,28 +1,5 @@
#include "parser.h"
-#include <stdarg.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <android-base/logging.h>
-
-void parse_error(struct parse_state *state, const char *fmt, ...)
-{
- va_list ap;
- char buf[128];
- int off;
-
- snprintf(buf, sizeof(buf), "%s: %d: ", state->filename, state->line);
- buf[127] = 0;
- off = strlen(buf);
-
- va_start(ap, fmt);
- vsnprintf(buf + off, 128 - off, fmt, ap);
- va_end(ap);
- buf[127] = 0;
- LOG(ERROR) << buf;
-}
-
int next_token(struct parse_state *state)
{
char *x = state->ptr;
diff --git a/init/parser.h b/init/parser.h
index 95e1164..3dcc566 100644
--- a/init/parser.h
+++ b/init/parser.h
@@ -27,14 +27,8 @@
char *text;
int line;
int nexttoken;
- void *context;
- void (*parse_line)(struct parse_state *state, int nargs, char **args);
- const char *filename;
- void *priv;
};
-void dump_parser_state(void);
int next_token(struct parse_state *state);
-void parse_error(struct parse_state *state, const char *fmt, ...);
#endif /* PARSER_H_ */
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 20a2aa1..aa47976 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -43,6 +43,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <bootimg.h>
@@ -573,10 +574,28 @@
}
}
+// persist.sys.usb.config values can't be combined on build-time when property
+// files are split into each partition.
+// So we need to apply the same rule of build/make/tools/post_process_props.py
+// on runtime.
+static void update_sys_usb_config() {
+ bool is_debuggable = android::base::GetBoolProperty("ro.debuggable", false);
+ std::string config = android::base::GetProperty("persist.sys.usb.config", "");
+ if (config.empty()) {
+ property_set("persist.sys.usb.config", is_debuggable ? "adb" : "none");
+ } else if (is_debuggable && config.find("adb") == std::string::npos &&
+ config.length() + 4 < PROP_VALUE_MAX) {
+ config.append(",adb");
+ property_set("persist.sys.usb.config", config);
+ }
+}
+
void property_load_boot_defaults() {
load_properties_from_file("/default.prop", NULL);
load_properties_from_file("/odm/default.prop", NULL);
load_properties_from_file("/vendor/default.prop", NULL);
+
+ update_sys_usb_config();
}
static void load_override_properties() {
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 80a6bd2..d6baa26 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -205,7 +205,7 @@
return true;
}
-static void DumpUmountDebuggingInfo() {
+static void DumpUmountDebuggingInfo(bool dump_all) {
int status;
if (!security_getenforce()) {
LOG(INFO) << "Run lsof";
@@ -214,6 +214,10 @@
true, nullptr, nullptr, 0);
}
FindPartitionsToUmount(nullptr, nullptr, true);
+ if (dump_all) {
+ // dump current tasks, this log can be lengthy, so only dump with dump_all
+ android::base::WriteStringToFile("t", "/proc/sysrq-trigger");
+ }
}
static UmountStat UmountPartitions(int timeoutMs) {
@@ -277,11 +281,11 @@
UmountStat stat = UmountPartitions(timeoutMs - t.duration_ms());
if (stat != UMOUNT_STAT_SUCCESS) {
LOG(INFO) << "umount timeout, last resort, kill all and try";
- if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo();
+ if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(false);
KillAllProcesses();
// even if it succeeds, still it is timeout and do not run fsck with all processes killed
UmountPartitions(0);
- if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo();
+ if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(true);
}
if (stat == UMOUNT_STAT_SUCCESS && runFsck) {
diff --git a/init/service.cpp b/init/service.cpp
index 4adbbf0..2284a21 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -157,6 +157,7 @@
gid_(0),
namespace_flags_(0),
seclabel_(""),
+ onrestart_(false, "<Service '" + name + "' onrestart>", 0),
ioprio_class_(IoSchedClass_NONE),
ioprio_pri_(0),
priority_(0),
@@ -180,6 +181,7 @@
capabilities_(capabilities),
namespace_flags_(namespace_flags),
seclabel_(seclabel),
+ onrestart_(false, "<Service '" + name + "' onrestart>", 0),
ioprio_class_(IoSchedClass_NONE),
ioprio_pri_(0),
priority_(0),
@@ -215,7 +217,7 @@
r = killProcessGroup(uid_, pid_, signal);
}
if (r == -1) {
- PLOG(ERROR) << "killProcessGroup(" << uid_ << ", " << pid_ << ", " << signal << ") failed";
+ LOG(ERROR) << "killProcessGroup(" << uid_ << ", " << pid_ << ", " << signal << ") failed";
}
if (kill(-pid_, signal) == -1) {
PLOG(ERROR) << "kill(" << pid_ << ", " << signal << ") failed";
@@ -438,7 +440,8 @@
bool Service::ParseOnrestart(const std::vector<std::string>& args, std::string* err) {
std::vector<std::string> str_args(args.begin() + 1, args.end());
- onrestart_.AddCommand(str_args, "", 0, err);
+ int line = onrestart_.NumCommands() + 1;
+ onrestart_.AddCommand(str_args, line, err);
return true;
}
@@ -531,14 +534,14 @@
}
class Service::OptionParserMap : public KeywordMap<OptionParser> {
-public:
- OptionParserMap() {
- }
-private:
- Map& map() const override;
+ public:
+ OptionParserMap() {}
+
+ private:
+ const Map& map() const override;
};
-Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
+const Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
static const Map option_parsers = {
@@ -569,13 +572,8 @@
}
bool Service::ParseLine(const std::vector<std::string>& args, std::string* err) {
- if (args.empty()) {
- *err = "option needed, but not provided";
- return false;
- }
-
static const OptionParserMap parser_map;
- auto parser = parser_map.FindFunction(args[0], args.size() - 1, err);
+ auto parser = parser_map.FindFunction(args, err);
if (!parser) {
return false;
@@ -636,7 +634,6 @@
if (!seclabel_.empty()) {
scon = seclabel_;
} else {
- LOG(INFO) << "computing context for service '" << name_ << "'";
scon = ComputeContextFromExecutable(name_, args_[0]);
if (scon == "") {
return false;
@@ -874,11 +871,6 @@
}
void ServiceManager::AddService(std::unique_ptr<Service> service) {
- Service* old_service = FindServiceByName(service->name());
- if (old_service) {
- LOG(ERROR) << "ignored duplicate definition of service '" << service->name() << "'";
- return;
- }
services_.emplace_back(std::move(service));
}
@@ -933,7 +925,9 @@
std::vector<std::string> str_args(args.begin() + command_arg, args.end());
exec_count_++;
- std::string name = StringPrintf("exec %d (%s)", exec_count_, str_args[0].c_str());
+ std::string name =
+ "exec " + std::to_string(exec_count_) + " (" + android::base::Join(str_args, " ") + ")";
+
unsigned flags = SVC_EXEC | SVC_ONESHOT | SVC_TEMPORARY;
CapSet no_capabilities;
unsigned namespace_flags = 0;
@@ -1092,8 +1086,8 @@
}
}
-bool ServiceParser::ParseSection(const std::vector<std::string>& args,
- std::string* err) {
+bool ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
+ int line, std::string* err) {
if (args.size() < 3) {
*err = "services must have a name and a program";
return false;
@@ -1105,20 +1099,24 @@
return false;
}
+ Service* old_service = service_manager_->FindServiceByName(name);
+ if (old_service) {
+ *err = "ignored duplicate definition of service '" + name + "'";
+ return false;
+ }
+
std::vector<std::string> str_args(args.begin() + 2, args.end());
service_ = std::make_unique<Service>(name, str_args);
return true;
}
-bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const {
- return service_ ? service_->ParseLine(args, err) : false;
+bool ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
+ return service_ ? service_->ParseLine(std::move(args), err) : false;
}
void ServiceParser::EndSection() {
if (service_) {
- ServiceManager::GetInstance().AddService(std::move(service_));
+ service_manager_->AddService(std::move(service_));
}
}
diff --git a/init/service.h b/init/service.h
index 5e89b9f..634fe4e 100644
--- a/init/service.h
+++ b/init/service.h
@@ -212,20 +212,18 @@
};
class ServiceParser : public SectionParser {
-public:
- ServiceParser() : service_(nullptr) {
- }
- bool ParseSection(const std::vector<std::string>& args,
+ public:
+ ServiceParser(ServiceManager* service_manager)
+ : service_manager_(service_manager), service_(nullptr) {}
+ bool ParseSection(std::vector<std::string>&& args, const std::string& filename, int line,
std::string* err) override;
- bool ParseLineSection(const std::vector<std::string>& args,
- const std::string& filename, int line,
- std::string* err) const override;
+ bool ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) override;
void EndSection() override;
- void EndFile(const std::string&) override {
- }
-private:
+
+ private:
bool IsValidName(const std::string& name) const;
+ ServiceManager* service_manager_;
std::unique_ptr<Service> service_;
};
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index b6c6a01..963cc4d 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -18,9 +18,7 @@
#include <ctype.h>
#include <fcntl.h>
-#include <grp.h>
#include <poll.h>
-#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
@@ -33,9 +31,11 @@
#include "devices.h"
#include "log.h"
-#include "ueventd_parser.h"
#include "util.h"
+template <bool sysfs>
+static bool ParseSingleLine(std::vector<std::string>&& line, std::string* err);
+
int ueventd_main(int argc, char **argv)
{
/*
@@ -60,9 +60,14 @@
cb.func_log = selinux_klog_callback;
selinux_set_callback(SELINUX_CB_LOG, cb);
- ueventd_parse_config_file("/ueventd.rc");
- ueventd_parse_config_file("/vendor/ueventd.rc");
- ueventd_parse_config_file("/odm/ueventd.rc");
+ Parser& parser = Parser::GetInstance();
+ parser.AddSectionParser("subsystem", std::make_unique<SubsystemParser>());
+ using namespace std::placeholders;
+ parser.AddSingleLineParser("/sys/", std::bind(ParsePermissionsLine, _1, _2, true));
+ parser.AddSingleLineParser("/dev/", std::bind(ParsePermissionsLine, _1, _2, false));
+ parser.ParseConfig("/ueventd.rc");
+ parser.ParseConfig("/vendor/ueventd.rc");
+ parser.ParseConfig("/odm/ueventd.rc");
/*
* keep the current product name base configuration so
@@ -72,7 +77,7 @@
* device node entries (b/34968103)
*/
std::string hardware = android::base::GetProperty("ro.hardware", "");
- ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware.c_str()).c_str());
+ parser.ParseConfig("/ueventd." + hardware + ".rc");
device_init();
@@ -93,59 +98,3 @@
return 0;
}
-
-void set_device_permission(const char* fn, int line, int nargs, char **args)
-{
- char *name;
- char *attr = 0;
- mode_t perm;
- uid_t uid;
- gid_t gid;
- char *endptr;
-
- if (nargs == 0)
- return;
-
- if (args[0][0] == '#')
- return;
-
- name = args[0];
-
- if (!strncmp(name,"/sys/", 5) && (nargs == 5)) {
- LOG(INFO) << "/sys/ rule " << args[0] << " " << args[1];
- attr = args[1];
- args++;
- nargs--;
- }
-
- if (nargs != 4) {
- LOG(ERROR) << "invalid line (" << fn << ":" << line << ") line for '" << args[0] << "'";
- return;
- }
-
- perm = strtol(args[1], &endptr, 8);
- if (!endptr || *endptr != '\0') {
- LOG(ERROR) << "invalid mode (" << fn << ":" << line << ") '" << args[1] << "'";
- return;
- }
-
- struct passwd* pwd = getpwnam(args[2]);
- if (!pwd) {
- LOG(ERROR) << "invalid uid (" << fn << ":" << line << ") '" << args[2] << "'";
- return;
- }
- uid = pwd->pw_uid;
-
- struct group* grp = getgrnam(args[3]);
- if (!grp) {
- LOG(ERROR) << "invalid gid (" << fn << ":" << line << ") '" << args[3] << "'";
- return;
- }
- gid = grp->gr_gid;
-
- if (attr) {
- sysfs_permissions.emplace_back(name, attr, perm, uid, gid);
- } else {
- dev_permissions.emplace_back(name, perm, uid, gid);
- }
-}
diff --git a/init/ueventd.h b/init/ueventd.h
index d44d1ca..1f424d3 100644
--- a/init/ueventd.h
+++ b/init/ueventd.h
@@ -17,24 +17,6 @@
#ifndef _INIT_UEVENTD_H_
#define _INIT_UEVENTD_H_
-#include <sys/types.h>
-
-#include <cutils/list.h>
-
-enum devname_src_t {
- DEVNAME_UNKNOWN = 0,
- DEVNAME_UEVENT_DEVNAME,
- DEVNAME_UEVENT_DEVPATH,
-};
-
-struct ueventd_subsystem {
- struct listnode slist;
-
- const char *name;
- const char *dirname;
- devname_src_t devname_src;
-};
-
-int ueventd_main(int argc, char **argv);
+int ueventd_main(int argc, char** argv);
#endif
diff --git a/init/ueventd_keywords.h b/init/ueventd_keywords.h
deleted file mode 100644
index 88e8f01..0000000
--- a/init/ueventd_keywords.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef KEYWORD
-#define __MAKE_KEYWORD_ENUM__
-#define KEYWORD(symbol, flags, nargs) K_##symbol,
-enum {
- K_UNKNOWN,
-#endif
- KEYWORD(subsystem, SECTION, 1)
- KEYWORD(devname, OPTION, 1)
- KEYWORD(dirname, OPTION, 1)
-#ifdef __MAKE_KEYWORD_ENUM__
- KEYWORD_COUNT,
-};
-#undef __MAKE_KEYWORD_ENUM__
-#undef KEYWORD
-#endif
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
deleted file mode 100644
index 510d7bb..0000000
--- a/init/ueventd_parser.cpp
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ueventd_parser.h"
-
-#include <ctype.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-
-#include "parser.h"
-#include "util.h"
-
-static list_declare(subsystem_list);
-
-static void parse_line_device(struct parse_state *state, int nargs, char **args);
-
-#define SECTION 0x01
-#define OPTION 0x02
-
-#include "ueventd_keywords.h"
-
-#define KEYWORD(symbol, flags, nargs) \
- [ K_##symbol ] = { #symbol, (nargs) + 1, flags, },
-
-static struct {
- const char *name;
- unsigned char nargs;
- unsigned char flags;
-} keyword_info[KEYWORD_COUNT] = {
- [ K_UNKNOWN ] = { "unknown", 0, 0 },
-#include "ueventd_keywords.h"
-};
-#undef KEYWORD
-
-#define kw_is(kw, type) (keyword_info[kw].flags & (type))
-#define kw_nargs(kw) (keyword_info[kw].nargs)
-
-static int lookup_keyword(const char *s)
-{
- switch (*s++) {
- case 'd':
- if (!strcmp(s, "evname")) return K_devname;
- if (!strcmp(s, "irname")) return K_dirname;
- break;
- case 's':
- if (!strcmp(s, "ubsystem")) return K_subsystem;
- break;
- }
- return K_UNKNOWN;
-}
-
-static void parse_line_no_op(struct parse_state*, int, char**) {
-}
-
-static int valid_name(const char *name)
-{
- while (*name) {
- if (!isalnum(*name) && (*name != '_') && (*name != '-')) {
- return 0;
- }
- name++;
- }
- return 1;
-}
-
-struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name)
-{
- struct listnode *node;
- struct ueventd_subsystem *s;
-
- list_for_each(node, &subsystem_list) {
- s = node_to_item(node, struct ueventd_subsystem, slist);
- if (!strcmp(s->name, name)) {
- return s;
- }
- }
- return 0;
-}
-
-static void *parse_subsystem(parse_state* state, int /*nargs*/, char** args) {
- if (!valid_name(args[1])) {
- parse_error(state, "invalid subsystem name '%s'\n", args[1]);
- return 0;
- }
-
- ueventd_subsystem* s = ueventd_subsystem_find_by_name(args[1]);
- if (s) {
- parse_error(state, "ignored duplicate definition of subsystem '%s'\n",
- args[1]);
- return 0;
- }
-
- s = (ueventd_subsystem*) calloc(1, sizeof(*s));
- if (!s) {
- parse_error(state, "out of memory\n");
- return 0;
- }
- s->name = args[1];
- s->dirname = "/dev";
- list_add_tail(&subsystem_list, &s->slist);
- return s;
-}
-
-static void parse_line_subsystem(struct parse_state *state, int nargs,
- char **args)
-{
- struct ueventd_subsystem *s = (ueventd_subsystem*) state->context;
- int kw;
-
- if (nargs == 0) {
- return;
- }
-
- kw = lookup_keyword(args[0]);
- switch (kw) {
- case K_devname:
- if (!strcmp(args[1], "uevent_devname"))
- s->devname_src = DEVNAME_UEVENT_DEVNAME;
- else if (!strcmp(args[1], "uevent_devpath"))
- s->devname_src = DEVNAME_UEVENT_DEVPATH;
- else
- parse_error(state, "invalid devname '%s'\n", args[1]);
- break;
-
- case K_dirname:
- if (args[1][0] == '/')
- s->dirname = args[1];
- else
- parse_error(state, "dirname '%s' does not start with '/'\n",
- args[1]);
- break;
-
- default:
- parse_error(state, "invalid option '%s'\n", args[0]);
- }
-}
-
-static void parse_new_section(struct parse_state *state, int kw,
- int nargs, char **args)
-{
- printf("[ %s %s ]\n", args[0],
- nargs > 1 ? args[1] : "");
-
- switch(kw) {
- case K_subsystem:
- state->context = parse_subsystem(state, nargs, args);
- if (state->context) {
- state->parse_line = parse_line_subsystem;
- return;
- }
- break;
- }
- state->parse_line = parse_line_no_op;
-}
-
-static void parse_line(struct parse_state *state, char **args, int nargs)
-{
- int kw = lookup_keyword(args[0]);
- int kw_nargs = kw_nargs(kw);
-
- if (nargs < kw_nargs) {
- parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
- kw_nargs > 2 ? "arguments" : "argument");
- return;
- }
-
- if (kw_is(kw, SECTION)) {
- parse_new_section(state, kw, nargs, args);
- } else if (kw_is(kw, OPTION)) {
- state->parse_line(state, nargs, args);
- } else {
- parse_line_device(state, nargs, args);
- }
-}
-
-static void parse_config(const char *fn, const std::string& data)
-{
- char *args[UEVENTD_PARSER_MAXARGS];
-
- int nargs = 0;
- parse_state state;
- state.filename = fn;
- state.line = 1;
- state.ptr = strdup(data.c_str()); // TODO: fix this code!
- state.nexttoken = 0;
- state.parse_line = parse_line_no_op;
- for (;;) {
- int token = next_token(&state);
- switch (token) {
- case T_EOF:
- parse_line(&state, args, nargs);
- return;
- case T_NEWLINE:
- if (nargs) {
- parse_line(&state, args, nargs);
- nargs = 0;
- }
- state.line++;
- break;
- case T_TEXT:
- if (nargs < UEVENTD_PARSER_MAXARGS) {
- args[nargs++] = state.text;
- }
- break;
- }
- }
-}
-
-int ueventd_parse_config_file(const char *fn)
-{
- std::string data;
- if (!read_file(fn, &data)) {
- return -1;
- }
-
- data.push_back('\n'); // TODO: fix parse_config.
- parse_config(fn, data);
- return 0;
-}
-
-static void parse_line_device(parse_state* state, int nargs, char** args) {
- set_device_permission(state->filename, state->line, nargs, args);
-}
diff --git a/init/util.cpp b/init/util.cpp
index 32ae93d..a101ce5 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -369,3 +369,26 @@
os << t.duration_s() << " seconds";
return os;
}
+
+// Reads the content of device tree file under kAndroidDtDir directory.
+// Returns true if the read is success, false otherwise.
+bool read_android_dt_file(const std::string& sub_path, std::string* dt_content) {
+ const std::string file_name = kAndroidDtDir + sub_path;
+ if (android::base::ReadFileToString(file_name, dt_content)) {
+ if (!dt_content->empty()) {
+ dt_content->pop_back(); // Trims the trailing '\0' out.
+ return true;
+ }
+ }
+ return false;
+}
+
+bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content) {
+ std::string dt_content;
+ if (read_android_dt_file(sub_path, &dt_content)) {
+ if (dt_content == expected_content) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/init/util.h b/init/util.h
index 0bb9cdf..92b3a1d 100644
--- a/init/util.h
+++ b/init/util.h
@@ -29,6 +29,8 @@
#define COLDBOOT_DONE "/dev/.coldboot_done"
+const std::string kAndroidDtDir("/proc/device-tree/firmware/android/");
+
using android::base::boot_clock;
using namespace std::chrono_literals;
@@ -72,4 +74,8 @@
void panic() __attribute__((__noreturn__));
+// Reads or compares the content of device tree file under kAndroidDtDir directory.
+bool read_android_dt_file(const std::string& sub_path, std::string* dt_content);
+bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);
+
#endif
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 7de72a8..4a525be 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -62,6 +62,7 @@
cc_library {
name: "libbacktrace",
+ vendor_available: true,
defaults: ["libbacktrace_common"],
host_supported: true,
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 0a3bab7..58170ec 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -30,16 +30,14 @@
]
cc_library_headers {
- name: "libcutils_vndk_headers",
- host_supported: true,
- export_include_dirs: ["include_vndk"],
-}
-
-cc_library_headers {
name: "libcutils_headers",
+ vendor_available: true,
host_supported: true,
export_include_dirs: ["include"],
target: {
+ vendor: {
+ export_include_dirs: ["include_vndk"],
+ },
linux_bionic: {
enabled: true,
},
@@ -51,6 +49,7 @@
cc_library {
name: "libcutils",
+ vendor_available: true,
host_supported: true,
srcs: [
"config_utils.c",
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 5fc2386..e1e8c8d 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -158,7 +158,6 @@
{ 00555, AID_ROOT, AID_ROOT, 0, "system/etc/ppp/*" },
{ 00555, AID_ROOT, AID_ROOT, 0, "system/etc/rc.*" },
{ 00440, AID_ROOT, AID_ROOT, 0, "system/etc/recovery.img" },
- { 00440, AID_RADIO, AID_ROOT, 0, "system/etc/xtables.lock" },
{ 00600, AID_ROOT, AID_ROOT, 0, "vendor/build.prop" },
{ 00600, AID_ROOT, AID_ROOT, 0, "vendor/default.prop" },
{ 00444, AID_ROOT, AID_ROOT, 0, ven_conf_dir + 1 },
@@ -190,8 +189,10 @@
CAP_MASK_LONG(CAP_NET_RAW),
"system/bin/hostapd" },
- /* Support Bluetooth legacy hal accessing /sys/class/rfkill */
- { 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN),
+ /* Support Bluetooth legacy hal accessing /sys/class/rfkill
+ * Support RT scheduling in Bluetooth */
+ { 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN) |
+ CAP_MASK_LONG(CAP_SYS_NICE),
"vendor/bin/hw/android.hardware.bluetooth@1.0-service" },
/* Support wifi_hal_legacy administering a network interface. */
diff --git a/libcutils/include_vndk/cutils/log.h b/libcutils/include_vndk/cutils/log.h
index ae74024..21dc11e 100644
--- a/libcutils/include_vndk/cutils/log.h
+++ b/libcutils/include_vndk/cutils/log.h
@@ -16,6 +16,32 @@
*/
#ifndef _LIBS_CUTIL_LOG_H
#define _LIBS_CUTIL_LOG_H
-#warning "Deprecated: don't include cutils/log.h, use either android/log.h or log/log.h"
+
+/* We do not know if developer wanted log/log.h or subset android/log.h */
#include <log/log.h>
+
+#if defined(__GNUC__)
+#if defined( __clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-W#warnings"
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpedantic"
+#elif (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR > 9))
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-W#warnings"
+#else
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-Wcpp"
+#endif
+#endif
+
+#warning "Deprecated: don't include cutils/log.h, use either android/log.h or log/log.h"
+
+#if defined(__GNUC__)
+#if defined( __clang__)
+#pragma clang diagnostic pop
+#endif
+#pragma GCC diagnostic pop
+#endif
+
#endif /* _LIBS_CUTIL_LOG_H */
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
index 4a0b035..217733a 100644
--- a/libcutils/sched_policy.cpp
+++ b/libcutils/sched_policy.cpp
@@ -263,26 +263,26 @@
char grpBuf[32];
- if (cpusets_enabled()) {
+ grpBuf[0] = '\0';
+ if (schedboost_enabled()) {
+ if (getCGroupSubsys(tid, "schedtune", grpBuf, sizeof(grpBuf)) < 0) return -1;
+ }
+ if ((grpBuf[0] == '\0') && cpusets_enabled()) {
if (getCGroupSubsys(tid, "cpuset", grpBuf, sizeof(grpBuf)) < 0) return -1;
- if (grpBuf[0] == '\0') {
- *policy = SP_FOREGROUND;
- } else if (!strcmp(grpBuf, "foreground")) {
- *policy = SP_FOREGROUND;
- } else if (!strcmp(grpBuf, "system-background")) {
- *policy = SP_SYSTEM;
- } else if (!strcmp(grpBuf, "background")) {
- *policy = SP_BACKGROUND;
- } else if (!strcmp(grpBuf, "top-app")) {
- *policy = SP_TOP_APP;
- } else {
- errno = ERANGE;
- return -1;
- }
- } else {
- // In b/34193533, we removed bg_non_interactive cgroup, so now
- // all threads are in FOREGROUND cgroup
+ }
+ if (grpBuf[0] == '\0') {
*policy = SP_FOREGROUND;
+ } else if (!strcmp(grpBuf, "foreground")) {
+ *policy = SP_FOREGROUND;
+ } else if (!strcmp(grpBuf, "system-background")) {
+ *policy = SP_SYSTEM;
+ } else if (!strcmp(grpBuf, "background")) {
+ *policy = SP_BACKGROUND;
+ } else if (!strcmp(grpBuf, "top-app")) {
+ *policy = SP_TOP_APP;
+ } else {
+ errno = ERANGE;
+ return -1;
}
return 0;
}
diff --git a/liblog/include/log/log_time.h b/liblog/include/log/log_time.h
index 9ece0b3..3764faf 100644
--- a/liblog/include/log/log_time.h
+++ b/liblog/include/log/log_time.h
@@ -22,6 +22,8 @@
/* struct log_time is a wire-format variant of struct timespec */
#define NS_PER_SEC 1000000000ULL
+#define US_PER_SEC 1000000ULL
+#define MS_PER_SEC 1000ULL
#ifndef __struct_log_time_defined
#define __struct_log_time_defined
@@ -148,6 +150,14 @@
uint64_t nsec() const {
return static_cast<uint64_t>(tv_sec) * NS_PER_SEC + tv_nsec;
}
+ uint64_t usec() const {
+ return static_cast<uint64_t>(tv_sec) * US_PER_SEC +
+ tv_nsec / (NS_PER_SEC / US_PER_SEC);
+ }
+ uint64_t msec() const {
+ return static_cast<uint64_t>(tv_sec) * MS_PER_SEC +
+ tv_nsec / (NS_PER_SEC / MS_PER_SEC);
+ }
#ifdef _SYSTEM_CORE_INCLUDE_PRIVATE_ANDROID_LOGGER_H_
static const char default_format[];
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 70b8a28..ec32da0 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1839,6 +1839,7 @@
// that it can be determined the property is not set.
static const char nothing_val[] = "_NOTHING_TO_SEE_HERE_";
char persist[PROP_VALUE_MAX];
+ char persist_hold[PROP_VALUE_MAX];
char readonly[PROP_VALUE_MAX];
// First part of this test requires the test itself to have the appropriate
@@ -1846,14 +1847,16 @@
// bail rather than give a failing grade.
property_get(persist_key, persist, "");
fprintf(stderr, "INFO: getprop %s -> %s\n", persist_key, persist);
+ strncpy(persist_hold, persist, PROP_VALUE_MAX);
property_get(readonly_key, readonly, nothing_val);
fprintf(stderr, "INFO: getprop %s -> %s\n", readonly_key, readonly);
if (!strcmp(readonly, nothing_val)) {
+ // Lets check if we can set the value (we should not be allowed to do so)
EXPECT_FALSE(__android_log_security());
fprintf(stderr, "WARNING: setting ro.device_owner to a domain\n");
static const char domain[] = "com.google.android.SecOps.DeviceOwner";
- property_set(readonly_key, domain);
+ EXPECT_NE(0, property_set(readonly_key, domain));
useconds_t total_time = 0;
static const useconds_t seconds = 1000000;
static const useconds_t max_time = 5 * seconds; // not going to happen
@@ -1870,9 +1873,12 @@
break;
}
}
- EXPECT_STREQ(readonly, domain);
- } else if (!strcasecmp(readonly, "false") || !readonly[0]) {
- // not enough permissions to run
+ EXPECT_STRNE(domain, readonly);
+ }
+
+ if (!strcasecmp(readonly, "false") || !readonly[0] ||
+ !strcmp(readonly, nothing_val)) {
+ // not enough permissions to run tests surrounding persist.logd.security
EXPECT_FALSE(__android_log_security());
return;
}
@@ -1883,16 +1889,51 @@
EXPECT_FALSE(__android_log_security());
}
property_set(persist_key, "TRUE");
- EXPECT_TRUE(__android_log_security());
+ property_get(persist_key, persist, "");
+ uid_t uid = getuid();
+ gid_t gid = getgid();
+ bool perm = (gid == AID_ROOT) || (uid == AID_ROOT);
+ EXPECT_STREQ(perm ? "TRUE" : persist_hold, persist);
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
property_set(persist_key, "FALSE");
- EXPECT_FALSE(__android_log_security());
+ property_get(persist_key, persist, "");
+ EXPECT_STREQ(perm ? "FALSE" : persist_hold, persist);
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
property_set(persist_key, "true");
- EXPECT_TRUE(__android_log_security());
+ property_get(persist_key, persist, "");
+ EXPECT_STREQ(perm ? "true" : persist_hold, persist);
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
property_set(persist_key, "false");
- EXPECT_FALSE(__android_log_security());
+ property_get(persist_key, persist, "");
+ EXPECT_STREQ(perm ? "false" : persist_hold, persist);
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
property_set(persist_key, "");
- EXPECT_FALSE(__android_log_security());
- property_set(persist_key, persist);
+ property_get(persist_key, persist, "");
+ EXPECT_STREQ(perm ? "" : persist_hold, persist);
+ if (!strcasecmp(persist, "true")) {
+ EXPECT_TRUE(__android_log_security());
+ } else {
+ EXPECT_FALSE(__android_log_security());
+ }
+ property_set(persist_key, persist_hold);
+ property_get(persist_key, persist, "");
+ EXPECT_STREQ(persist_hold, persist);
#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
diff --git a/libnetutils/Android.bp b/libnetutils/Android.bp
new file mode 100644
index 0000000..f710ba2
--- /dev/null
+++ b/libnetutils/Android.bp
@@ -0,0 +1,19 @@
+cc_library_shared {
+ name: "libnetutils",
+
+ srcs: [
+ "dhcpclient.c",
+ "dhcpmsg.c",
+ "ifc_utils.c",
+ "packet.c",
+ ],
+
+ shared_libs: [
+ "libcutils",
+ "liblog",
+ ],
+
+ cflags: ["-Werror"],
+
+ export_include_dirs: ["include"],
+}
diff --git a/libnetutils/Android.mk b/libnetutils/Android.mk
deleted file mode 100644
index 2150279..0000000
--- a/libnetutils/Android.mk
+++ /dev/null
@@ -1,21 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- dhcpclient.c \
- dhcpmsg.c \
- ifc_utils.c \
- packet.c
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- liblog
-
-LOCAL_MODULE := libnetutils
-
-LOCAL_CFLAGS := -Werror
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
new file mode 100644
index 0000000..37e4dbf
--- /dev/null
+++ b/libprocessgroup/Android.bp
@@ -0,0 +1,10 @@
+cc_library {
+ srcs: ["processgroup.cpp"],
+ name: "libprocessgroup",
+ shared_libs: ["libbase"],
+ export_include_dirs: ["include"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/libprocessgroup/Android.mk b/libprocessgroup/Android.mk
deleted file mode 100644
index 0bfc391..0000000
--- a/libprocessgroup/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := processgroup.cpp
-LOCAL_MODULE := libprocessgroup
-LOCAL_STATIC_LIBRARIES := libbase
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror
-include $(BUILD_STATIC_LIBRARY)
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := processgroup.cpp
-LOCAL_MODULE := libprocessgroup
-LOCAL_SHARED_LIBRARIES := libbase
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Wall -Werror
-include $(BUILD_SHARED_LIBRARY)
diff --git a/libsync/Android.bp b/libsync/Android.bp
index a4e5599..4bafb08 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -8,6 +8,7 @@
cc_library_shared {
name: "libsync",
+ vendor_available: true,
defaults: ["libsync_defaults"],
}
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
new file mode 100644
index 0000000..296bd26
--- /dev/null
+++ b/libsysutils/Android.bp
@@ -0,0 +1,25 @@
+cc_library_shared {
+ name: "libsysutils",
+ srcs: [
+ "src/SocketListener.cpp",
+ "src/FrameworkListener.cpp",
+ "src/NetlinkListener.cpp",
+ "src/NetlinkEvent.cpp",
+ "src/FrameworkCommand.cpp",
+ "src/SocketClient.cpp",
+ "src/ServiceManager.cpp",
+ ],
+
+ logtags: ["EventLogTags.logtags"],
+
+ cflags: ["-Werror"],
+
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "liblog",
+ "libnl",
+ ],
+
+ export_include_dirs: ["include"],
+}
diff --git a/libsysutils/Android.mk b/libsysutils/Android.mk
deleted file mode 100644
index 584e5a2..0000000
--- a/libsysutils/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- src/SocketListener.cpp \
- src/FrameworkListener.cpp \
- src/NetlinkListener.cpp \
- src/NetlinkEvent.cpp \
- src/FrameworkCommand.cpp \
- src/SocketClient.cpp \
- src/ServiceManager.cpp \
- EventLogTags.logtags
-
-LOCAL_MODULE:= libsysutils
-
-LOCAL_CFLAGS := -Werror
-
-LOCAL_SHARED_LIBRARIES := \
- libbase \
- libcutils \
- liblog \
- libnl
-
-LOCAL_EXPORT_C_INCLUDE_DIRS := system/core/libsysutils/include
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index e35593f..32ed6c3 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -47,6 +47,7 @@
srcs: [
"ArmExidx.cpp",
+ "DwarfCfa.cpp",
"DwarfMemory.cpp",
"DwarfOp.cpp",
"Elf.cpp",
@@ -54,6 +55,7 @@
"ElfInterfaceArm.cpp",
"Log.cpp",
"Regs.cpp",
+ "MapInfo.cpp",
"Maps.cpp",
"Memory.cpp",
"Symbols.cpp",
@@ -91,6 +93,8 @@
srcs: [
"tests/ArmExidxDecodeTest.cpp",
"tests/ArmExidxExtractTest.cpp",
+ "tests/DwarfCfaLogTest.cpp",
+ "tests/DwarfCfaTest.cpp",
"tests/DwarfMemoryTest.cpp",
"tests/DwarfOpLogTest.cpp",
"tests/DwarfOpTest.cpp",
@@ -98,6 +102,7 @@
"tests/ElfInterfaceTest.cpp",
"tests/ElfTest.cpp",
"tests/LogFake.cpp",
+ "tests/MapInfoTest.cpp",
"tests/MapsTest.cpp",
"tests/MemoryFake.cpp",
"tests/MemoryFileTest.cpp",
diff --git a/libunwindstack/DwarfCfa.cpp b/libunwindstack/DwarfCfa.cpp
new file mode 100644
index 0000000..006f039
--- /dev/null
+++ b/libunwindstack/DwarfCfa.cpp
@@ -0,0 +1,713 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <inttypes.h>
+#include <stdint.h>
+
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+
+#include "DwarfCfa.h"
+#include "DwarfEncoding.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "DwarfStructs.h"
+#include "Log.h"
+
+template <typename AddressType>
+constexpr typename DwarfCfa<AddressType>::process_func DwarfCfa<AddressType>::kCallbackTable[64];
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
+ dwarf_loc_regs_t* loc_regs) {
+ if (cie_loc_regs_ != nullptr) {
+ for (const auto& entry : *cie_loc_regs_) {
+ (*loc_regs)[entry.first] = entry.second;
+ }
+ }
+ last_error_ = DWARF_ERROR_NONE;
+
+ 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) {
+ operands_.clear();
+ // Read the cfa information.
+ uint8_t cfa_value;
+ if (!memory_->ReadBytes(&cfa_value, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ uint8_t cfa_low = cfa_value & 0x3f;
+ // Check the 2 high bits.
+ switch (cfa_value >> 6) {
+ case 1:
+ cur_pc_ += cfa_low * fde_->cie->code_alignment_factor;
+ break;
+ case 2: {
+ uint64_t offset;
+ if (!memory_->ReadULEB128(&offset)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ SignedType signed_offset =
+ static_cast<SignedType>(offset) * fde_->cie->data_alignment_factor;
+ (*loc_regs)[cfa_low] = {.type = DWARF_LOCATION_OFFSET,
+ .values = {static_cast<uint64_t>(signed_offset)}};
+ break;
+ }
+ case 3: {
+ if (cie_loc_regs_ == nullptr) {
+ log(0, "restore while processing cie");
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+
+ auto reg_entry = cie_loc_regs_->find(cfa_low);
+ if (reg_entry == cie_loc_regs_->end()) {
+ loc_regs->erase(cfa_low);
+ } else {
+ (*loc_regs)[cfa_low] = reg_entry->second;
+ }
+ break;
+ }
+ case 0: {
+ const auto handle_func = DwarfCfa<AddressType>::kCallbackTable[cfa_low];
+ if (handle_func == nullptr) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ const auto cfa = &DwarfCfaInfo::kTable[cfa_low];
+ for (size_t i = 0; i < cfa->num_operands; i++) {
+ if (cfa->operands[i] == DW_EH_PE_block) {
+ uint64_t block_length;
+ if (!memory_->ReadULEB128(&block_length)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ operands_.push_back(block_length);
+ memory_->set_cur_offset(memory_->cur_offset() + block_length);
+ continue;
+ }
+ uint64_t value;
+ if (!memory_->ReadEncodedValue<AddressType>(cfa->operands[i], &value)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ operands_.push_back(value);
+ }
+
+ if (!(this->*handle_func)(loc_regs)) {
+ return false;
+ }
+ break;
+ }
+ }
+ }
+ return true;
+}
+
+template <typename AddressType>
+std::string DwarfCfa<AddressType>::GetOperandString(uint8_t operand, uint64_t value,
+ uint64_t* cur_pc) {
+ std::string string;
+ switch (operand) {
+ case DwarfCfaInfo::DWARF_DISPLAY_REGISTER:
+ string = " register(" + std::to_string(value) + ")";
+ break;
+ case DwarfCfaInfo::DWARF_DISPLAY_SIGNED_NUMBER:
+ string += " " + std::to_string(static_cast<SignedType>(value));
+ break;
+ case DwarfCfaInfo::DWARF_DISPLAY_ADVANCE_LOC:
+ *cur_pc += value;
+ // Fall through to log the value.
+ case DwarfCfaInfo::DWARF_DISPLAY_NUMBER:
+ string += " " + std::to_string(value);
+ break;
+ case DwarfCfaInfo::DWARF_DISPLAY_SET_LOC:
+ *cur_pc = value;
+ // Fall through to log the value.
+ case DwarfCfaInfo::DWARF_DISPLAY_ADDRESS:
+ if (std::is_same<AddressType, uint32_t>::value) {
+ string += android::base::StringPrintf(" 0x%" PRIx32, static_cast<uint32_t>(value));
+ } else {
+ string += android::base::StringPrintf(" 0x%" PRIx64, static_cast<uint64_t>(value));
+ }
+ break;
+ default:
+ string = " unknown";
+ }
+ return string;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::LogOffsetRegisterString(uint32_t indent, uint64_t cfa_offset,
+ uint8_t reg) {
+ uint64_t offset;
+ if (!memory_->ReadULEB128(&offset)) {
+ return false;
+ }
+ uint64_t end_offset = memory_->cur_offset();
+ memory_->set_cur_offset(cfa_offset);
+
+ std::string raw_data = "Raw Data:";
+ for (uint64_t i = cfa_offset; i < end_offset; i++) {
+ uint8_t value;
+ if (!memory_->ReadBytes(&value, 1)) {
+ return false;
+ }
+ raw_data += android::base::StringPrintf(" 0x%02x", value);
+ }
+ log(indent, "DW_CFA_offset register(%d) %" PRId64, reg, offset);
+ log(indent, "%s", raw_data.c_str());
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op,
+ uint64_t* cur_pc) {
+ const auto* cfa = &DwarfCfaInfo::kTable[op];
+ if (cfa->name == nullptr) {
+ log(indent, "Illegal");
+ log(indent, "Raw Data: 0x%02x", op);
+ return true;
+ }
+
+ std::string log_string(cfa->name);
+ std::vector<std::string> expression_lines;
+ for (size_t i = 0; i < cfa->num_operands; i++) {
+ if (cfa->operands[i] == DW_EH_PE_block) {
+ // This is a Dwarf Expression.
+ uint64_t end_offset;
+ if (!memory_->ReadULEB128(&end_offset)) {
+ return false;
+ }
+ log_string += " " + std::to_string(end_offset);
+ end_offset += memory_->cur_offset();
+
+ DwarfOp<AddressType> op(memory_, nullptr);
+ op.GetLogInfo(memory_->cur_offset(), end_offset, &expression_lines);
+ memory_->set_cur_offset(end_offset);
+ } else {
+ uint64_t value;
+ if (!memory_->ReadEncodedValue<AddressType>(cfa->operands[i], &value)) {
+ return false;
+ }
+ log_string += GetOperandString(cfa->display_operands[i], value, cur_pc);
+ }
+ }
+ log(indent, "%s", log_string.c_str());
+
+ // Get the raw bytes of the data.
+ uint64_t end_offset = memory_->cur_offset();
+ memory_->set_cur_offset(cfa_offset);
+ std::string raw_data("Raw Data:");
+ for (uint64_t i = 0; i < end_offset - cfa_offset; i++) {
+ uint8_t value;
+ if (!memory_->ReadBytes(&value, 1)) {
+ return false;
+ }
+
+ // Only show 10 raw bytes per line.
+ if ((i % 10) == 0 && i != 0) {
+ log(indent, "%s", raw_data.c_str());
+ raw_data.clear();
+ }
+ if (raw_data.empty()) {
+ raw_data = "Raw Data:";
+ }
+ raw_data += android::base::StringPrintf(" 0x%02x", value);
+ }
+ if (!raw_data.empty()) {
+ log(indent, "%s", raw_data.c_str());
+ }
+
+ // Log any of the expression data.
+ for (const auto line : expression_lines) {
+ log(indent + 1, "%s", line.c_str());
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::Log(uint32_t indent, uint64_t pc, uint64_t load_bias,
+ uint64_t start_offset, uint64_t end_offset) {
+ memory_->set_cur_offset(start_offset);
+ uint64_t cfa_offset;
+ uint64_t cur_pc = fde_->pc_start;
+ uint64_t old_pc = cur_pc;
+ while ((cfa_offset = memory_->cur_offset()) < end_offset && cur_pc <= pc) {
+ // Read the cfa information.
+ uint8_t cfa_value;
+ if (!memory_->ReadBytes(&cfa_value, 1)) {
+ return false;
+ }
+
+ // Check the 2 high bits.
+ uint8_t cfa_low = cfa_value & 0x3f;
+ switch (cfa_value >> 6) {
+ case 0:
+ if (!LogInstruction(indent, cfa_offset, cfa_low, &cur_pc)) {
+ return false;
+ }
+ break;
+ case 1:
+ log(indent, "DW_CFA_advance_loc %d", cfa_low);
+ log(indent, "Raw Data: 0x%02x", cfa_value);
+ cur_pc += cfa_low * fde_->cie->code_alignment_factor;
+ break;
+ case 2:
+ if (!LogOffsetRegisterString(indent, cfa_offset, cfa_low)) {
+ return false;
+ }
+ break;
+ case 3:
+ log(indent, "DW_CFA_restore register(%d)", cfa_low);
+ log(indent, "Raw Data: 0x%02x", cfa_value);
+ break;
+ }
+ if (cur_pc != old_pc) {
+ log(indent, "");
+ log(indent, "PC 0x%" PRIx64, cur_pc + load_bias);
+ }
+ old_pc = cur_pc;
+ }
+ return true;
+}
+
+// Static data.
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_nop(dwarf_loc_regs_t*) {
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_set_loc(dwarf_loc_regs_t*) {
+ AddressType cur_pc = cur_pc_;
+ AddressType new_pc = operands_[0];
+ if (new_pc < cur_pc) {
+ if (std::is_same<AddressType, uint32_t>::value) {
+ log(0, "Warning: PC is moving backwards: old 0x%" PRIx32 " new 0x%" PRIx32, cur_pc, new_pc);
+ } else {
+ log(0, "Warning: PC is moving backwards: old 0x%" PRIx64 " new 0x%" PRIx64, cur_pc, new_pc);
+ }
+ }
+ cur_pc_ = new_pc;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_advance_loc(dwarf_loc_regs_t*) {
+ cur_pc_ += operands_[0] * fde_->cie->code_alignment_factor;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_offset(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {operands_[1]}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_restore(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ if (cie_loc_regs_ == nullptr) {
+ log(0, "restore while processing cie");
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+ auto reg_entry = cie_loc_regs_->find(reg);
+ if (reg_entry == cie_loc_regs_->end()) {
+ loc_regs->erase(reg);
+ } else {
+ (*loc_regs)[reg] = reg_entry->second;
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_undefined(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_UNDEFINED};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_same_value(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ loc_regs->erase(reg);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_register(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ AddressType reg_dst = operands_[1];
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_REGISTER, .values = {reg_dst}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_remember_state(dwarf_loc_regs_t* loc_regs) {
+ loc_reg_state_.push(*loc_regs);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_restore_state(dwarf_loc_regs_t* loc_regs) {
+ if (loc_reg_state_.size() == 0) {
+ log(0, "Warning: Attempt to restore without remember.");
+ return true;
+ }
+ *loc_regs = loc_reg_state_.top();
+ loc_reg_state_.pop();
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa(dwarf_loc_regs_t* loc_regs) {
+ (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {operands_[0], operands_[1]}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_register(dwarf_loc_regs_t* loc_regs) {
+ auto cfa_location = loc_regs->find(CFA_REG);
+ if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
+ log(0, "Attempt to set new register, but cfa is not already set to a register.");
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+
+ cfa_location->second.values[0] = operands_[0];
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_offset(dwarf_loc_regs_t* loc_regs) {
+ // Changing the offset if this is not a register is illegal.
+ auto cfa_location = loc_regs->find(CFA_REG);
+ if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
+ log(0, "Attempt to set offset, but cfa is not set to a register.");
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+ cfa_location->second.values[1] = operands_[0];
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_expression(dwarf_loc_regs_t* loc_regs) {
+ (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_EXPRESSION,
+ .values = {operands_[0], memory_->cur_offset()}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_expression(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_EXPRESSION,
+ .values = {operands_[1], memory_->cur_offset()}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_offset_extended_sf(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ SignedType value = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {static_cast<uint64_t>(value)}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_sf(dwarf_loc_regs_t* loc_regs) {
+ SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
+ (*loc_regs)[CFA_REG] = {.type = DWARF_LOCATION_REGISTER,
+ .values = {operands_[0], static_cast<uint64_t>(offset)}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_def_cfa_offset_sf(dwarf_loc_regs_t* loc_regs) {
+ // Changing the offset if this is not a register is illegal.
+ auto cfa_location = loc_regs->find(CFA_REG);
+ if (cfa_location == loc_regs->end() || cfa_location->second.type != DWARF_LOCATION_REGISTER) {
+ log(0, "Attempt to set offset, but cfa is not set to a register.");
+ last_error_ = DWARF_ERROR_ILLEGAL_STATE;
+ return false;
+ }
+ SignedType offset = static_cast<SignedType>(operands_[0]) * fde_->cie->data_alignment_factor;
+ cfa_location->second.values[1] = static_cast<uint64_t>(offset);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_val_offset(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_OFFSET, .values = {static_cast<uint64_t>(offset)}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_val_offset_sf(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ SignedType offset = static_cast<SignedType>(operands_[1]) * fde_->cie->data_alignment_factor;
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_OFFSET, .values = {static_cast<uint64_t>(offset)}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_val_expression(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_VAL_EXPRESSION,
+ .values = {operands_[1], memory_->cur_offset()}};
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfCfa<AddressType>::cfa_gnu_negative_offset_extended(dwarf_loc_regs_t* loc_regs) {
+ AddressType reg = operands_[0];
+ SignedType offset = -static_cast<SignedType>(operands_[1]);
+ (*loc_regs)[reg] = {.type = DWARF_LOCATION_OFFSET, .values = {static_cast<uint64_t>(offset)}};
+ return true;
+}
+
+const DwarfCfaInfo::Info DwarfCfaInfo::kTable[64] = {
+ {
+ // 0x00 DW_CFA_nop
+ "DW_CFA_nop",
+ 2,
+ 0,
+ {},
+ {},
+ },
+ {
+ "DW_CFA_set_loc", // 0x01 DW_CFA_set_loc
+ 2,
+ 1,
+ {DW_EH_PE_absptr},
+ {DWARF_DISPLAY_SET_LOC},
+ },
+ {
+ "DW_CFA_advance_loc1", // 0x02 DW_CFA_advance_loc1
+ 2,
+ 1,
+ {DW_EH_PE_udata1},
+ {DWARF_DISPLAY_ADVANCE_LOC},
+ },
+ {
+ "DW_CFA_advance_loc2", // 0x03 DW_CFA_advance_loc2
+ 2,
+ 1,
+ {DW_EH_PE_udata2},
+ {DWARF_DISPLAY_ADVANCE_LOC},
+ },
+ {
+ "DW_CFA_advance_loc4", // 0x04 DW_CFA_advance_loc4
+ 2,
+ 1,
+ {DW_EH_PE_udata4},
+ {DWARF_DISPLAY_ADVANCE_LOC},
+ },
+ {
+ "DW_CFA_offset_extended", // 0x05 DW_CFA_offset_extended
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
+ },
+ {
+ "DW_CFA_restore_extended", // 0x06 DW_CFA_restore_extended
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER},
+ },
+ {
+ "DW_CFA_undefined", // 0x07 DW_CFA_undefined
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER},
+ },
+ {
+ "DW_CFA_same_value", // 0x08 DW_CFA_same_value
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER},
+ },
+ {
+ "DW_CFA_register", // 0x09 DW_CFA_register
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_REGISTER},
+ },
+ {
+ "DW_CFA_remember_state", // 0x0a DW_CFA_remember_state
+ 2,
+ 0,
+ {},
+ {},
+ },
+ {
+ "DW_CFA_restore_state", // 0x0b DW_CFA_restore_state
+ 2,
+ 0,
+ {},
+ {},
+ },
+ {
+ "DW_CFA_def_cfa", // 0x0c DW_CFA_def_cfa
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
+ },
+ {
+ "DW_CFA_def_cfa_register", // 0x0d DW_CFA_def_cfa_register
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER},
+ },
+ {
+ "DW_CFA_def_cfa_offset", // 0x0e DW_CFA_def_cfa_offset
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_NUMBER},
+ },
+ {
+ "DW_CFA_def_cfa_expression", // 0x0f DW_CFA_def_cfa_expression
+ 2,
+ 1,
+ {DW_EH_PE_block},
+ {DWARF_DISPLAY_EVAL_BLOCK},
+ },
+ {
+ "DW_CFA_expression", // 0x10 DW_CFA_expression
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_block},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_EVAL_BLOCK},
+ },
+ {
+ "DW_CFA_offset_extended_sf", // 0x11 DW_CFA_offset_extend_sf
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER},
+ },
+ {
+ "DW_CFA_def_cfa_sf", // 0x12 DW_CFA_def_cfa_sf
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER},
+ },
+ {
+ "DW_CFA_def_cfa_offset_sf", // 0x13 DW_CFA_def_cfa_offset_sf
+ 2,
+ 1,
+ {DW_EH_PE_sleb128},
+ {DWARF_DISPLAY_SIGNED_NUMBER},
+ },
+ {
+ "DW_CFA_val_offset", // 0x14 DW_CFA_val_offset
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
+ },
+ {
+ "DW_CFA_val_offset_sf", // 0x15 DW_CFA_val_offset_sf
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_SIGNED_NUMBER},
+ },
+ {
+ "DW_CFA_val_expression", // 0x16 DW_CFA_val_expression
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_block},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_EVAL_BLOCK},
+ },
+ {nullptr, 0, 0, {}, {}}, // 0x17 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x18 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x19 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x1a illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x1b illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x1c DW_CFA_lo_user (Treat as illegal)
+ {nullptr, 0, 0, {}, {}}, // 0x1d illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x1e illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x1f illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x20 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x21 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x22 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x23 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x24 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x25 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x26 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x27 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x28 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x29 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x2a illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x2b illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x2c illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x2d DW_CFA_GNU_window_save (Treat as illegal)
+ {
+ "DW_CFA_GNU_args_size", // 0x2e DW_CFA_GNU_args_size
+ 2,
+ 1,
+ {DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_NUMBER},
+ },
+ {
+ "DW_CFA_GNU_negative_offset_extended", // 0x2f DW_CFA_GNU_negative_offset_extended
+ 2,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ {DWARF_DISPLAY_REGISTER, DWARF_DISPLAY_NUMBER},
+ },
+ {nullptr, 0, 0, {}, {}}, // 0x31 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x32 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x33 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x34 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x35 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x36 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x37 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x38 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x39 illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x3a illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x3b illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x3c illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x3d illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x3e illegal cfa
+ {nullptr, 0, 0, {}, {}}, // 0x3f DW_CFA_hi_user (Treat as illegal)
+};
+
+// Explicitly instantiate DwarfCfa.
+template class DwarfCfa<uint32_t>;
+template class DwarfCfa<uint64_t>;
diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h
new file mode 100644
index 0000000..ce7da4a
--- /dev/null
+++ b/libunwindstack/DwarfCfa.h
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_CFA_H
+#define _LIBUNWINDSTACK_DWARF_CFA_H
+
+#include <stdint.h>
+
+#include <stack>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "DwarfError.h"
+#include "DwarfLocation.h"
+#include "DwarfMemory.h"
+#include "DwarfStructs.h"
+
+// DWARF Standard home: http://dwarfstd.org/
+// This code is based on DWARF 4: http://http://dwarfstd.org/doc/DWARF4.pdf
+// See section 6.4.2.1 for a description of the DW_CFA_xxx values.
+
+class DwarfCfaInfo {
+ public:
+ enum DisplayType : uint8_t {
+ DWARF_DISPLAY_NONE = 0,
+ DWARF_DISPLAY_REGISTER,
+ DWARF_DISPLAY_NUMBER,
+ DWARF_DISPLAY_SIGNED_NUMBER,
+ DWARF_DISPLAY_EVAL_BLOCK,
+ DWARF_DISPLAY_ADDRESS,
+ DWARF_DISPLAY_SET_LOC,
+ DWARF_DISPLAY_ADVANCE_LOC,
+ };
+
+ struct Info {
+ const char* name;
+ uint8_t supported_version;
+ uint8_t num_operands;
+ uint8_t operands[2];
+ uint8_t display_operands[2];
+ };
+
+ const static Info kTable[64];
+};
+
+template <typename AddressType>
+class DwarfCfa {
+ // Signed version of AddressType
+ typedef typename std::make_signed<AddressType>::type SignedType;
+
+ public:
+ DwarfCfa(DwarfMemory* memory, const DwarfFDE* fde) : memory_(memory), fde_(fde) {}
+ virtual ~DwarfCfa() = default;
+
+ bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
+ dwarf_loc_regs_t* loc_regs);
+
+ bool Log(uint32_t indent, uint64_t pc, uint64_t load_bias, uint64_t start_offset,
+ uint64_t end_offset);
+
+ DwarfError last_error() { return last_error_; }
+
+ AddressType cur_pc() { return cur_pc_; }
+
+ void set_cie_loc_regs(const dwarf_loc_regs_t* cie_loc_regs) { cie_loc_regs_ = cie_loc_regs; }
+
+ protected:
+ std::string GetOperandString(uint8_t operand, uint64_t value, uint64_t* cur_pc);
+
+ bool LogOffsetRegisterString(uint32_t indent, uint64_t cfa_offset, uint8_t reg);
+
+ bool LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op, uint64_t* cur_pc);
+
+ private:
+ DwarfError last_error_;
+ DwarfMemory* memory_;
+ const DwarfFDE* fde_;
+
+ AddressType cur_pc_;
+ const dwarf_loc_regs_t* cie_loc_regs_ = nullptr;
+ std::vector<AddressType> operands_;
+ std::stack<dwarf_loc_regs_t> loc_reg_state_;
+
+ // CFA processing functions.
+ bool cfa_nop(dwarf_loc_regs_t*);
+ bool cfa_set_loc(dwarf_loc_regs_t*);
+ bool cfa_advance_loc(dwarf_loc_regs_t*);
+ bool cfa_offset(dwarf_loc_regs_t*);
+ bool cfa_restore(dwarf_loc_regs_t*);
+ bool cfa_undefined(dwarf_loc_regs_t*);
+ bool cfa_same_value(dwarf_loc_regs_t*);
+ bool cfa_register(dwarf_loc_regs_t*);
+ bool cfa_remember_state(dwarf_loc_regs_t*);
+ bool cfa_restore_state(dwarf_loc_regs_t*);
+ bool cfa_def_cfa(dwarf_loc_regs_t*);
+ bool cfa_def_cfa_register(dwarf_loc_regs_t*);
+ bool cfa_def_cfa_offset(dwarf_loc_regs_t*);
+ bool cfa_def_cfa_expression(dwarf_loc_regs_t*);
+ bool cfa_expression(dwarf_loc_regs_t*);
+ bool cfa_offset_extended_sf(dwarf_loc_regs_t*);
+ bool cfa_def_cfa_sf(dwarf_loc_regs_t*);
+ bool cfa_def_cfa_offset_sf(dwarf_loc_regs_t*);
+ bool cfa_val_offset(dwarf_loc_regs_t*);
+ bool cfa_val_offset_sf(dwarf_loc_regs_t*);
+ bool cfa_val_expression(dwarf_loc_regs_t*);
+ bool cfa_gnu_negative_offset_extended(dwarf_loc_regs_t*);
+
+ using process_func = bool (DwarfCfa::*)(dwarf_loc_regs_t*);
+ constexpr static process_func kCallbackTable[64] = {
+ // 0x00 DW_CFA_nop
+ &DwarfCfa::cfa_nop,
+ // 0x01 DW_CFA_set_loc
+ &DwarfCfa::cfa_set_loc,
+ // 0x02 DW_CFA_advance_loc1
+ &DwarfCfa::cfa_advance_loc,
+ // 0x03 DW_CFA_advance_loc2
+ &DwarfCfa::cfa_advance_loc,
+ // 0x04 DW_CFA_advance_loc4
+ &DwarfCfa::cfa_advance_loc,
+ // 0x05 DW_CFA_offset_extended
+ &DwarfCfa::cfa_offset,
+ // 0x06 DW_CFA_restore_extended
+ &DwarfCfa::cfa_restore,
+ // 0x07 DW_CFA_undefined
+ &DwarfCfa::cfa_undefined,
+ // 0x08 DW_CFA_same_value
+ &DwarfCfa::cfa_same_value,
+ // 0x09 DW_CFA_register
+ &DwarfCfa::cfa_register,
+ // 0x0a DW_CFA_remember_state
+ &DwarfCfa::cfa_remember_state,
+ // 0x0b DW_CFA_restore_state
+ &DwarfCfa::cfa_restore_state,
+ // 0x0c DW_CFA_def_cfa
+ &DwarfCfa::cfa_def_cfa,
+ // 0x0d DW_CFA_def_cfa_register
+ &DwarfCfa::cfa_def_cfa_register,
+ // 0x0e DW_CFA_def_cfa_offset
+ &DwarfCfa::cfa_def_cfa_offset,
+ // 0x0f DW_CFA_def_cfa_expression
+ &DwarfCfa::cfa_def_cfa_expression,
+ // 0x10 DW_CFA_expression
+ &DwarfCfa::cfa_expression,
+ // 0x11 DW_CFA_offset_extended_sf
+ &DwarfCfa::cfa_offset_extended_sf,
+ // 0x12 DW_CFA_def_cfa_sf
+ &DwarfCfa::cfa_def_cfa_sf,
+ // 0x13 DW_CFA_def_cfa_offset_sf
+ &DwarfCfa::cfa_def_cfa_offset_sf,
+ // 0x14 DW_CFA_val_offset
+ &DwarfCfa::cfa_val_offset,
+ // 0x15 DW_CFA_val_offset_sf
+ &DwarfCfa::cfa_val_offset_sf,
+ // 0x16 DW_CFA_val_expression
+ &DwarfCfa::cfa_val_expression,
+ // 0x17 illegal cfa
+ nullptr,
+ // 0x18 illegal cfa
+ nullptr,
+ // 0x19 illegal cfa
+ nullptr,
+ // 0x1a illegal cfa
+ nullptr,
+ // 0x1b illegal cfa
+ nullptr,
+ // 0x1c DW_CFA_lo_user (Treat this as illegal)
+ nullptr,
+ // 0x1d illegal cfa
+ nullptr,
+ // 0x1e illegal cfa
+ nullptr,
+ // 0x1f illegal cfa
+ nullptr,
+ // 0x20 illegal cfa
+ nullptr,
+ // 0x21 illegal cfa
+ nullptr,
+ // 0x22 illegal cfa
+ nullptr,
+ // 0x23 illegal cfa
+ nullptr,
+ // 0x24 illegal cfa
+ nullptr,
+ // 0x25 illegal cfa
+ nullptr,
+ // 0x26 illegal cfa
+ nullptr,
+ // 0x27 illegal cfa
+ nullptr,
+ // 0x28 illegal cfa
+ nullptr,
+ // 0x29 illegal cfa
+ nullptr,
+ // 0x2a illegal cfa
+ nullptr,
+ // 0x2b illegal cfa
+ nullptr,
+ // 0x2c illegal cfa
+ nullptr,
+ // 0x2d DW_CFA_GNU_window_save (Treat this as illegal)
+ nullptr,
+ // 0x2e DW_CFA_GNU_args_size
+ &DwarfCfa::cfa_nop,
+ // 0x2f DW_CFA_GNU_negative_offset_extended
+ &DwarfCfa::cfa_gnu_negative_offset_extended,
+ // 0x30 illegal cfa
+ nullptr,
+ // 0x31 illegal cfa
+ nullptr,
+ // 0x32 illegal cfa
+ nullptr,
+ // 0x33 illegal cfa
+ nullptr,
+ // 0x34 illegal cfa
+ nullptr,
+ // 0x35 illegal cfa
+ nullptr,
+ // 0x36 illegal cfa
+ nullptr,
+ // 0x37 illegal cfa
+ nullptr,
+ // 0x38 illegal cfa
+ nullptr,
+ // 0x39 illegal cfa
+ nullptr,
+ // 0x3a illegal cfa
+ nullptr,
+ // 0x3b illegal cfa
+ nullptr,
+ // 0x3c illegal cfa
+ nullptr,
+ // 0x3d illegal cfa
+ nullptr,
+ // 0x3e illegal cfa
+ nullptr,
+ // 0x3f DW_CFA_hi_user (Treat this as illegal)
+ nullptr,
+ };
+};
+
+#endif // _LIBUNWINDSTACK_DWARF_CFA_H
diff --git a/libunwindstack/DwarfLocation.h b/libunwindstack/DwarfLocation.h
new file mode 100644
index 0000000..062d125
--- /dev/null
+++ b/libunwindstack/DwarfLocation.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_LOCATION_H
+#define _LIBUNWINDSTACK_DWARF_LOCATION_H
+
+#include <stdint.h>
+
+#include <unordered_map>
+
+enum DwarfLocationEnum : uint8_t {
+ DWARF_LOCATION_INVALID = 0,
+ DWARF_LOCATION_UNDEFINED,
+ DWARF_LOCATION_OFFSET,
+ DWARF_LOCATION_VAL_OFFSET,
+ DWARF_LOCATION_REGISTER,
+ DWARF_LOCATION_EXPRESSION,
+ DWARF_LOCATION_VAL_EXPRESSION,
+};
+
+struct DwarfLocation {
+ DwarfLocationEnum type;
+ uint64_t values[2];
+};
+
+typedef std::unordered_map<uint16_t, DwarfLocation> dwarf_loc_regs_t;
+
+#endif // _LIBUNWINDSTACK_DWARF_LOCATION_H
diff --git a/libunwindstack/DwarfStructs.h b/libunwindstack/DwarfStructs.h
new file mode 100644
index 0000000..57aac88
--- /dev/null
+++ b/libunwindstack/DwarfStructs.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_DWARF_STRUCTS_H
+#define _LIBUNWINDSTACK_DWARF_STRUCTS_H
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "DwarfEncoding.h"
+
+struct DwarfCIE {
+ uint8_t version = 0;
+ uint8_t fde_address_encoding = DW_EH_PE_absptr;
+ uint8_t lsda_encoding = DW_EH_PE_omit;
+ uint8_t segment_size = 0;
+ std::vector<char> augmentation_string;
+ uint64_t personality_handler = 0;
+ uint64_t cfa_instructions_offset = 0;
+ uint64_t cfa_instructions_end = 0;
+ uint64_t code_alignment_factor = 0;
+ int64_t data_alignment_factor = 0;
+ uint64_t return_address_register = 0;
+};
+
+struct DwarfFDE {
+ uint64_t cie_offset = 0;
+ uint64_t cfa_instructions_offset = 0;
+ uint64_t cfa_instructions_end = 0;
+ uint64_t pc_start = 0;
+ uint64_t pc_end = 0;
+ uint64_t lsda_address = 0;
+ const DwarfCIE* cie = nullptr;
+};
+
+constexpr uint16_t CFA_REG = static_cast<uint16_t>(-1);
+
+#endif // _LIBUNWINDSTACK_DWARF_STRUCTS_H
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
new file mode 100644
index 0000000..051f700
--- /dev/null
+++ b/libunwindstack/MapInfo.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+#include "Elf.h"
+#include "MapInfo.h"
+#include "Maps.h"
+#include "Memory.h"
+
+Memory* MapInfo::CreateMemory(pid_t pid) {
+ if (end <= start) {
+ return nullptr;
+ }
+
+ elf_offset = 0;
+
+ // First try and use the file associated with the info.
+ if (!name.empty()) {
+ // Fail on device maps.
+ if (flags & MAPS_FLAGS_DEVICE_MAP) {
+ return nullptr;
+ }
+
+ std::unique_ptr<MemoryFileAtOffset> file_memory(new MemoryFileAtOffset);
+ uint64_t map_size;
+ if (offset != 0) {
+ // Only map in a piece of the file.
+ map_size = end - start;
+ } else {
+ map_size = UINT64_MAX;
+ }
+ if (file_memory->Init(name, offset, map_size)) {
+ // It's possible that a non-zero offset might not be pointing to
+ // valid elf data. Check if this is a valid elf, and if not assume
+ // that this was meant to incorporate the entire file.
+ if (offset != 0 && !Elf::IsValidElf(file_memory.get())) {
+ // Don't bother checking the validity that will happen on the elf init.
+ if (file_memory->Init(name, 0)) {
+ elf_offset = offset;
+ return file_memory.release();
+ }
+ // Fall through if the init fails.
+ } else {
+ return file_memory.release();
+ }
+ }
+ }
+
+ Memory* memory = nullptr;
+ if (pid == getpid()) {
+ memory = new MemoryLocal();
+ } else {
+ memory = new MemoryRemote(pid);
+ }
+ return new MemoryRange(memory, start, end);
+}
+
+Elf* MapInfo::GetElf(pid_t pid, bool) {
+ if (elf) {
+ return elf;
+ }
+
+ elf = new Elf(CreateMemory(pid));
+ elf->Init();
+ // If the init fails, keep the elf around as an invalid object so we
+ // don't try to reinit the object.
+ return elf;
+}
diff --git a/libunwindstack/MapInfo.h b/libunwindstack/MapInfo.h
index 8342904..79a2ada 100644
--- a/libunwindstack/MapInfo.h
+++ b/libunwindstack/MapInfo.h
@@ -39,6 +39,7 @@
uint64_t elf_offset;
Memory* CreateMemory(pid_t pid);
+ Elf* GetElf(pid_t pid, bool init_gnu_debugdata = false);
};
#endif // _LIBUNWINDSTACK_MAP_INFO_H
diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp
new file mode 100644
index 0000000..3185bc3
--- /dev/null
+++ b/libunwindstack/tests/DwarfCfaLogTest.cpp
@@ -0,0 +1,814 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <memory>
+#include <type_traits>
+#include <unordered_map>
+
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include "DwarfCfa.h"
+#include "DwarfLocation.h"
+#include "DwarfMemory.h"
+#include "DwarfStructs.h"
+#include "Log.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+template <typename TypeParam>
+class DwarfCfaLogTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ memory_.Clear();
+
+ dmem_.reset(new DwarfMemory(&memory_));
+
+ cie_.cfa_instructions_offset = 0x1000;
+ cie_.cfa_instructions_end = 0x1030;
+ // These two values should be different to distinguish between
+ // operations that deal with code versus data.
+ cie_.code_alignment_factor = 4;
+ cie_.data_alignment_factor = 8;
+
+ fde_.cfa_instructions_offset = 0x2000;
+ fde_.cfa_instructions_end = 0x2030;
+ fde_.pc_start = 0x2000;
+ fde_.pc_end = 0x2000;
+ fde_.pc_end = 0x10000;
+ fde_.cie = &cie_;
+ cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_));
+ }
+
+ MemoryFake memory_;
+ std::unique_ptr<DwarfMemory> dmem_;
+ std::unique_ptr<DwarfCfa<TypeParam>> cfa_;
+ DwarfCIE cie_;
+ DwarfFDE fde_;
+};
+TYPED_TEST_CASE_P(DwarfCfaLogTest);
+
+// NOTE: All class variable references have to be prefaced with this->.
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_illegal) {
+ for (uint8_t i = 0x17; i < 0x3f; i++) {
+ if (i == 0x2e || i == 0x2f) {
+ // Skip gnu extension ops.
+ continue;
+ }
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+ std::string expected = "4 unwind Illegal\n";
+ expected += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", i);
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+ }
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_nop) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x00});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+ std::string expected =
+ "4 unwind DW_CFA_nop\n"
+ "4 unwind Raw Data: 0x00\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_offset) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x83, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2002));
+ std::string expected =
+ "4 unwind DW_CFA_offset register(3) 4\n"
+ "4 unwind Raw Data: 0x83 0x04\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x83, 0x84, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2100, 0x2103));
+ expected =
+ "4 unwind DW_CFA_offset register(3) 132\n"
+ "4 unwind Raw Data: 0x83 0x84 0x01\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x05, 0x03, 0x02});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+ std::string expected =
+ "4 unwind DW_CFA_offset_extended register(3) 2\n"
+ "4 unwind Raw Data: 0x05 0x03 0x02\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x05, 0x81, 0x01, 0x82, 0x12});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+ expected =
+ "4 unwind DW_CFA_offset_extended register(129) 2306\n"
+ "4 unwind Raw Data: 0x05 0x81 0x01 0x82 0x12\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_offset_extended_sf) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x11, 0x05, 0x10});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+ std::string expected =
+ "4 unwind DW_CFA_offset_extended_sf register(5) 16\n"
+ "4 unwind Raw Data: 0x11 0x05 0x10\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Check a negative value for the offset.
+ ResetLogs();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x11, 0x86, 0x01, 0xff, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+ expected =
+ "4 unwind DW_CFA_offset_extended_sf register(134) -1\n"
+ "4 unwind Raw Data: 0x11 0x86 0x01 0xff 0x7f\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_restore) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0xc2});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2001));
+ std::string expected =
+ "4 unwind DW_CFA_restore register(2)\n"
+ "4 unwind Raw Data: 0xc2\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x82, 0x04, 0xc2});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x3000, 0x3003));
+ expected =
+ "4 unwind DW_CFA_offset register(2) 4\n"
+ "4 unwind Raw Data: 0x82 0x04\n"
+ "4 unwind DW_CFA_restore register(2)\n"
+ "4 unwind Raw Data: 0xc2\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_restore_extended) {
+ this->memory_.SetMemory(0x4000, std::vector<uint8_t>{0x06, 0x08});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4000, 0x4002));
+ std::string expected =
+ "4 unwind DW_CFA_restore_extended register(8)\n"
+ "4 unwind Raw Data: 0x06 0x08\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x05, 0x82, 0x02, 0x04, 0x06, 0x82, 0x02});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x5000, 0x5007));
+ expected =
+ "4 unwind DW_CFA_offset_extended register(258) 4\n"
+ "4 unwind Raw Data: 0x05 0x82 0x02 0x04\n"
+ "4 unwind DW_CFA_restore_extended register(258)\n"
+ "4 unwind Raw Data: 0x06 0x82 0x02\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_set_loc) {
+ uint8_t buffer[1 + sizeof(TypeParam)];
+ buffer[0] = 0x1;
+ TypeParam address;
+ std::string raw_data("Raw Data: 0x01 ");
+ std::string address_str;
+ if (std::is_same<TypeParam, uint32_t>::value) {
+ address = 0x81234578U;
+ address_str = "0x81234578";
+ raw_data += "0x78 0x45 0x23 0x81";
+ } else {
+ address = 0x8123456712345678ULL;
+ address_str = "0x8123456712345678";
+ raw_data += "0x78 0x56 0x34 0x12 0x67 0x45 0x23 0x81";
+ }
+ memcpy(&buffer[1], &address, sizeof(address));
+
+ this->memory_.SetMemory(0x50, buffer, sizeof(buffer));
+ ResetLogs();
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x50, 0x51 + sizeof(TypeParam)));
+ std::string expected = "4 unwind DW_CFA_set_loc " + address_str + "\n";
+ expected += "4 unwind " + raw_data + "\n";
+ expected += "4 unwind \n";
+ expected += "4 unwind PC " + address_str + "\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Check for a set going back.
+ ResetLogs();
+ this->fde_.pc_start = address + 0x10;
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x50, 0x51 + sizeof(TypeParam)));
+ expected = "4 unwind DW_CFA_set_loc " + address_str + "\n";
+ expected += "4 unwind " + raw_data + "\n";
+ expected += "4 unwind \n";
+ expected += "4 unwind PC " + address_str + "\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc) {
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x44});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x201));
+ std::string expected =
+ "4 unwind DW_CFA_advance_loc 4\n"
+ "4 unwind Raw Data: 0x44\n"
+ "4 unwind \n"
+ "4 unwind PC 0x2010\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x100, 0x200, 0x201));
+ expected =
+ "4 unwind DW_CFA_advance_loc 4\n"
+ "4 unwind Raw Data: 0x44\n"
+ "4 unwind \n"
+ "4 unwind PC 0x2110\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc1) {
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x02, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x202));
+ std::string expected =
+ "4 unwind DW_CFA_advance_loc1 4\n"
+ "4 unwind Raw Data: 0x02 0x04\n"
+ "4 unwind \n"
+ "4 unwind PC 0x2004\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x10, 0x200, 0x202));
+ expected =
+ "4 unwind DW_CFA_advance_loc1 4\n"
+ "4 unwind Raw Data: 0x02 0x04\n"
+ "4 unwind \n"
+ "4 unwind PC 0x2014\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc2) {
+ this->memory_.SetMemory(0x600, std::vector<uint8_t>{0x03, 0x04, 0x03});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x600, 0x603));
+ std::string expected =
+ "4 unwind DW_CFA_advance_loc2 772\n"
+ "4 unwind Raw Data: 0x03 0x04 0x03\n"
+ "4 unwind \n"
+ "4 unwind PC 0x2304\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x1000, 0x600, 0x603));
+ expected =
+ "4 unwind DW_CFA_advance_loc2 772\n"
+ "4 unwind Raw Data: 0x03 0x04 0x03\n"
+ "4 unwind \n"
+ "4 unwind PC 0x3304\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_advance_loc4) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x04, 0x04, 0x03, 0x02, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x505));
+ std::string expected =
+ "4 unwind DW_CFA_advance_loc4 16909060\n"
+ "4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n"
+ "4 unwind \n"
+ "4 unwind PC 0x1022304\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x500, 0x505));
+ expected =
+ "4 unwind DW_CFA_advance_loc4 16909060\n"
+ "4 unwind Raw Data: 0x04 0x04 0x03 0x02 0x01\n"
+ "4 unwind \n"
+ "4 unwind PC 0x1024304\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_undefined) {
+ this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x07, 0x09});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xa02));
+ std::string expected =
+ "4 unwind DW_CFA_undefined register(9)\n"
+ "4 unwind Raw Data: 0x07 0x09\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ dwarf_loc_regs_t cie_loc_regs;
+ this->memory_.SetMemory(0x1a00, std::vector<uint8_t>{0x07, 0x81, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1a00, 0x1a03));
+ expected =
+ "4 unwind DW_CFA_undefined register(129)\n"
+ "4 unwind Raw Data: 0x07 0x81 0x01\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_same) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x08, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+ std::string expected =
+ "4 unwind DW_CFA_same_value register(127)\n"
+ "4 unwind Raw Data: 0x08 0x7f\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x08, 0xff, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2100, 0x2103));
+ expected =
+ "4 unwind DW_CFA_same_value register(255)\n"
+ "4 unwind Raw Data: 0x08 0xff 0x01\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_register) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x303));
+ std::string expected =
+ "4 unwind DW_CFA_register register(2) register(1)\n"
+ "4 unwind Raw Data: 0x09 0x02 0x01\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x09, 0xff, 0x01, 0xff, 0x03});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4300, 0x4305));
+ expected =
+ "4 unwind DW_CFA_register register(255) register(511)\n"
+ "4 unwind Raw Data: 0x09 0xff 0x01 0xff 0x03\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_state) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x0a});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x301));
+
+ std::string expected =
+ "4 unwind DW_CFA_remember_state\n"
+ "4 unwind Raw Data: 0x0a\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x0b});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x4300, 0x4301));
+
+ expected =
+ "4 unwind DW_CFA_restore_state\n"
+ "4 unwind Raw Data: 0x0b\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_state_cfa_offset_restore) {
+ this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x0a, 0x0e, 0x40, 0x0b});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x3000, 0x3004));
+
+ std::string expected =
+ "4 unwind DW_CFA_remember_state\n"
+ "4 unwind Raw Data: 0x0a\n"
+ "4 unwind DW_CFA_def_cfa_offset 64\n"
+ "4 unwind Raw Data: 0x0e 0x40\n"
+ "4 unwind DW_CFA_restore_state\n"
+ "4 unwind Raw Data: 0x0b\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0c, 0x7f, 0x74});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa register(127) 116\n"
+ "4 unwind Raw Data: 0x0c 0x7f 0x74\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0c, 0xff, 0x02, 0xf4, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x205));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa register(383) 628\n"
+ "4 unwind Raw Data: 0x0c 0xff 0x02 0xf4 0x04\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x12, 0x30, 0x25});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa_sf register(48) 37\n"
+ "4 unwind Raw Data: 0x12 0x30 0x25\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Test a negative value.
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x12, 0xa3, 0x01, 0xfa, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x205));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_sf register(163) -6\n"
+ "4 unwind Raw Data: 0x12 0xa3 0x01 0xfa 0x7f\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_register) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0d, 0x72});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa_register register(114)\n"
+ "4 unwind Raw Data: 0x0d 0x72\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0d, 0xf9, 0x20});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_register register(4217)\n"
+ "4 unwind Raw Data: 0x0d 0xf9 0x20\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0e, 0x59});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa_offset 89\n"
+ "4 unwind Raw Data: 0x0e 0x59\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_offset 89\n"
+ "4 unwind Raw Data: 0x0e 0x59\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0e, 0xd4, 0x0a});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_offset 1364\n"
+ "4 unwind Raw Data: 0x0e 0xd4 0x0a\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_offset_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x13, 0x23});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa_offset_sf 35\n"
+ "4 unwind Raw Data: 0x13 0x23\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x102));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_offset_sf 35\n"
+ "4 unwind Raw Data: 0x13 0x23\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Negative offset.
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x13, 0xf6, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x203));
+
+ expected =
+ "4 unwind DW_CFA_def_cfa_offset_sf -10\n"
+ "4 unwind Raw Data: 0x13 0xf6 0x7f\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_def_cfa_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0f, 0x04, 0x01, 0x02, 0x04, 0x05});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x106));
+
+ std::string expected =
+ "4 unwind DW_CFA_def_cfa_expression 4\n"
+ "4 unwind Raw Data: 0x0f 0x04 0x01 0x02 0x04 0x05\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0x01\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0x02\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0x04\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0x05\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x0f, 0x81, 0x01};
+ expected = "4 unwind Raw Data: 0x0f 0x81 0x01";
+ std::string op_string;
+ for (uint8_t i = 3; i < 132; i++) {
+ ops.push_back(0x05);
+ op_string +=
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0x05\n";
+ expected += " 0x05";
+ if (((i + 1) % 10) == 0) {
+ expected += "\n4 unwind Raw Data:";
+ }
+ }
+ expected += '\n';
+ this->memory_.SetMemory(0x200, ops);
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x284));
+
+ expected = "4 unwind DW_CFA_def_cfa_expression 129\n" + expected;
+ ASSERT_EQ(expected + op_string, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x10, 0x04, 0x02, 0xc0, 0xc1});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x105));
+
+ std::string expected =
+ "4 unwind DW_CFA_expression register(4) 2\n"
+ "4 unwind Raw Data: 0x10 0x04 0x02 0xc0 0xc1\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0xc0\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0xc1\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x10, 0xff, 0x01, 0x82, 0x01};
+ expected = "4 unwind Raw Data: 0x10 0xff 0x01 0x82 0x01";
+ std::string op_string;
+ for (uint8_t i = 5; i < 135; i++) {
+ ops.push_back(0xa0 + (i - 5) % 96);
+ op_string += "4 unwind Illegal\n";
+ op_string += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", ops.back());
+ expected += android::base::StringPrintf(" 0x%02x", ops.back());
+ if (((i + 1) % 10) == 0) {
+ expected += "\n4 unwind Raw Data:";
+ }
+ }
+ expected = "4 unwind DW_CFA_expression register(255) 130\n" + expected + "\n";
+
+ this->memory_.SetMemory(0x200, ops);
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x200, 0x287));
+
+ ASSERT_EQ(expected + op_string, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x14, 0x45, 0x54});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+
+ std::string expected =
+ "4 unwind DW_CFA_val_offset register(69) 84\n"
+ "4 unwind Raw Data: 0x14 0x45 0x54\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x400, std::vector<uint8_t>{0x14, 0xa2, 0x02, 0xb4, 0x05});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x400, 0x405));
+
+ expected =
+ "4 unwind DW_CFA_val_offset register(290) 692\n"
+ "4 unwind Raw Data: 0x14 0xa2 0x02 0xb4 0x05\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_val_offset_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x15, 0x56, 0x12});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x103));
+
+ std::string expected =
+ "4 unwind DW_CFA_val_offset_sf register(86) 18\n"
+ "4 unwind Raw Data: 0x15 0x56 0x12\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Negative value.
+ ResetLogs();
+ this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x15, 0xff, 0x01, 0xc0, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xa05));
+
+ expected =
+ "4 unwind DW_CFA_val_offset_sf register(255) -64\n"
+ "4 unwind Raw Data: 0x15 0xff 0x01 0xc0 0x7f\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_val_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x16, 0x05, 0x02, 0xb0, 0xb1});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x100, 0x105));
+
+ std::string expected =
+ "4 unwind DW_CFA_val_expression register(5) 2\n"
+ "4 unwind Raw Data: 0x16 0x05 0x02 0xb0 0xb1\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0xb0\n"
+ "4 unwind Illegal\n"
+ "4 unwind Raw Data: 0xb1\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x16, 0x83, 0x10, 0xa8, 0x01};
+ expected = "4 unwind Raw Data: 0x16 0x83 0x10 0xa8 0x01";
+ std::string op_string;
+ for (uint8_t i = 0; i < 168; i++) {
+ ops.push_back(0xa0 + (i % 96));
+ op_string += "4 unwind Illegal\n";
+ op_string += android::base::StringPrintf("4 unwind Raw Data: 0x%02x\n", ops.back());
+ expected += android::base::StringPrintf(" 0x%02x", ops.back());
+ if (((i + 6) % 10) == 0) {
+ expected += "\n4 unwind Raw Data:";
+ }
+ }
+ expected = "4 unwind DW_CFA_val_expression register(2051) 168\n" + expected + "\n";
+
+ this->memory_.SetMemory(0xa00, ops);
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0xa00, 0xaad));
+
+ ASSERT_EQ(expected + op_string, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_args_size) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2e, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x2000, 0x2002));
+
+ std::string expected =
+ "4 unwind DW_CFA_GNU_args_size 4\n"
+ "4 unwind Raw Data: 0x2e 0x04\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x2e, 0xa4, 0x80, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x5000, 0x5004));
+
+ expected =
+ "4 unwind DW_CFA_GNU_args_size 65572\n"
+ "4 unwind Raw Data: 0x2e 0xa4 0x80 0x04\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_gnu_negative_offset_extended) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x2f, 0x08, 0x10});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x500, 0x503));
+
+ std::string expected =
+ "4 unwind DW_CFA_GNU_negative_offset_extended register(8) 16\n"
+ "4 unwind Raw Data: 0x2f 0x08 0x10\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x2f, 0x81, 0x02, 0xff, 0x01});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x1500, 0x1505));
+
+ expected =
+ "4 unwind DW_CFA_GNU_negative_offset_extended register(257) 255\n"
+ "4 unwind Raw Data: 0x2f 0x81 0x02 0xff 0x01\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaLogTest, cfa_register_override) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01, 0x09, 0x02, 0x04});
+
+ ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0, 0x300, 0x306));
+
+ std::string expected =
+ "4 unwind DW_CFA_register register(2) register(1)\n"
+ "4 unwind Raw Data: 0x09 0x02 0x01\n"
+ "4 unwind DW_CFA_register register(2) register(4)\n"
+ "4 unwind Raw Data: 0x09 0x02 0x04\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfCfaLogTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
+ cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
+ cfa_advance_loc, cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4,
+ cfa_undefined, cfa_same, cfa_register, cfa_state,
+ cfa_state_cfa_offset_restore, cfa_def_cfa, cfa_def_cfa_sf,
+ cfa_def_cfa_register, cfa_def_cfa_offset, cfa_def_cfa_offset_sf,
+ cfa_def_cfa_expression, cfa_expression, cfa_val_offset,
+ cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
+ cfa_gnu_negative_offset_extended, cfa_register_override);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaLogTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaLogTest, DwarfCfaLogTestTypes);
diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp
new file mode 100644
index 0000000..6cf028a
--- /dev/null
+++ b/libunwindstack/tests/DwarfCfaTest.cpp
@@ -0,0 +1,959 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <memory>
+#include <unordered_map>
+
+#include <gtest/gtest.h>
+
+#include "DwarfCfa.h"
+#include "DwarfLocation.h"
+#include "DwarfMemory.h"
+#include "DwarfStructs.h"
+#include "Log.h"
+
+#include "LogFake.h"
+#include "MemoryFake.h"
+
+template <typename TypeParam>
+class DwarfCfaTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ memory_.Clear();
+
+ dmem_.reset(new DwarfMemory(&memory_));
+
+ cie_.cfa_instructions_offset = 0x1000;
+ cie_.cfa_instructions_end = 0x1030;
+ // These two values should be different to distinguish between
+ // operations that deal with code versus data.
+ cie_.code_alignment_factor = 4;
+ cie_.data_alignment_factor = 8;
+
+ fde_.cfa_instructions_offset = 0x2000;
+ fde_.cfa_instructions_end = 0x2030;
+ fde_.pc_start = 0x2000;
+ fde_.cie = &cie_;
+
+ cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_));
+ }
+
+ MemoryFake memory_;
+ std::unique_ptr<DwarfMemory> dmem_;
+ std::unique_ptr<DwarfCfa<TypeParam>> cfa_;
+ DwarfCIE cie_;
+ DwarfFDE fde_;
+};
+TYPED_TEST_CASE_P(DwarfCfaTest);
+
+// NOTE: All test class variables need to be referenced as this->.
+
+TYPED_TEST_P(DwarfCfaTest, cfa_illegal) {
+ for (uint8_t i = 0x17; i < 0x3f; i++) {
+ if (i == 0x2e || i == 0x2f) {
+ // Skip gnu extension ops.
+ continue;
+ }
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->cfa_->last_error());
+ ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+ }
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_nop) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x00});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+ ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+// This test needs to be examined.
+TYPED_TEST_P(DwarfCfaTest, cfa_offset) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x83, 0x04});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2002, &loc_regs));
+ ASSERT_EQ(0x2002U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(3);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(32U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x83, 0x84, 0x01});
+ loc_regs.clear();
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2100, 0x2103, &loc_regs));
+ ASSERT_EQ(0x2103U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(3);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(1056U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_offset_extended) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x05, 0x03, 0x02});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x503, &loc_regs));
+ ASSERT_EQ(0x503U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(3);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(2U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x05, 0x81, 0x01, 0x82, 0x12});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1500, 0x1505, &loc_regs));
+ ASSERT_EQ(0x1505U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(129);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(2306U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_offset_extended_sf) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x11, 0x05, 0x10});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x503, &loc_regs));
+ ASSERT_EQ(0x503U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(5);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(0x80U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Check a negative value for the offset.
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x11, 0x86, 0x01, 0xff, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1500, 0x1505, &loc_regs));
+ ASSERT_EQ(0x1505U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(134);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(static_cast<uint64_t>(-8), location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_restore) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0xc2});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+ ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("4 unwind restore while processing cie\n", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ dwarf_loc_regs_t cie_loc_regs;
+ cie_loc_regs[2] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}};
+ this->cfa_->set_cie_loc_regs(&cie_loc_regs);
+ this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x82, 0x04, 0xc2});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x3000, 0x3003, &loc_regs));
+ ASSERT_EQ(0x3003U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(2);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_restore_extended) {
+ this->memory_.SetMemory(0x4000, std::vector<uint8_t>{0x06, 0x08});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x4000, 0x4002, &loc_regs));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+ ASSERT_EQ(0x4002U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("4 unwind restore while processing cie\n", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x05, 0x82, 0x02, 0x04, 0x06, 0x82, 0x02});
+ dwarf_loc_regs_t cie_loc_regs;
+ cie_loc_regs[258] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}};
+ this->cfa_->set_cie_loc_regs(&cie_loc_regs);
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x5000, 0x5007, &loc_regs));
+ ASSERT_EQ(0x5007U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(258);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_set_loc) {
+ uint8_t buffer[1 + sizeof(TypeParam)];
+ buffer[0] = 0x1;
+ TypeParam address;
+ std::string raw_data("Raw Data: 0x01 ");
+ std::string address_str;
+ if (sizeof(TypeParam) == 4) {
+ address = 0x81234578U;
+ address_str = "0x81234578";
+ raw_data += "0x78 0x45 0x23 0x81";
+ } else {
+ address = 0x8123456712345678ULL;
+ address_str = "0x8123456712345678";
+ raw_data += "0x78 0x56 0x34 0x12 0x67 0x45 0x23 0x81";
+ }
+ memcpy(&buffer[1], &address, sizeof(address));
+
+ this->memory_.SetMemory(0x50, buffer, sizeof(buffer));
+ ResetLogs();
+ dwarf_loc_regs_t loc_regs;
+ ASSERT_TRUE(
+ this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam), &loc_regs));
+ ASSERT_EQ(0x51 + sizeof(TypeParam), this->dmem_->cur_offset());
+ ASSERT_EQ(address, this->cfa_->cur_pc());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Check for a set going back.
+ ResetLogs();
+ loc_regs.clear();
+ this->fde_.pc_start = address + 0x10;
+ ASSERT_TRUE(
+ this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x50, 0x51 + sizeof(TypeParam), &loc_regs));
+ ASSERT_EQ(0x51 + sizeof(TypeParam), this->dmem_->cur_offset());
+ ASSERT_EQ(address, this->cfa_->cur_pc());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ std::string cur_address_str(address_str);
+ cur_address_str[cur_address_str.size() - 2] = '8';
+ std::string expected = "4 unwind Warning: PC is moving backwards: old " + cur_address_str +
+ " new " + address_str + "\n";
+ ASSERT_EQ(expected, GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_advance_loc1) {
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x02, 0x04});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x202, &loc_regs));
+ ASSERT_EQ(0x202U, this->dmem_->cur_offset());
+ ASSERT_EQ(this->fde_.pc_start + 0x10, this->cfa_->cur_pc());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_advance_loc2) {
+ this->memory_.SetMemory(0x600, std::vector<uint8_t>{0x03, 0x04, 0x03});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x600, 0x603, &loc_regs));
+ ASSERT_EQ(0x603U, this->dmem_->cur_offset());
+ ASSERT_EQ(this->fde_.pc_start + 0xc10U, this->cfa_->cur_pc());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_advance_loc4) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x04, 0x04, 0x03, 0x02, 0x01});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x505, &loc_regs));
+ ASSERT_EQ(0x505U, this->dmem_->cur_offset());
+ ASSERT_EQ(this->fde_.pc_start + 0x4080c10, this->cfa_->cur_pc());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_undefined) {
+ this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x07, 0x09});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0xa00, 0xa02, &loc_regs));
+ ASSERT_EQ(0xa02U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(9);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_UNDEFINED, location->second.type);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x1a00, std::vector<uint8_t>{0x07, 0x81, 0x01});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1a00, 0x1a03, &loc_regs));
+ ASSERT_EQ(0x1a03U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(129);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_UNDEFINED, location->second.type);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_same) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x08, 0x7f});
+ dwarf_loc_regs_t loc_regs;
+
+ loc_regs[127] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}};
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0x102U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+ ASSERT_EQ(0U, loc_regs.count(127));
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x2100, std::vector<uint8_t>{0x08, 0xff, 0x01});
+
+ loc_regs[255] = {.type = DWARF_LOCATION_REGISTER, .values = {0, 0}};
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2100, 0x2103, &loc_regs));
+ ASSERT_EQ(0x2103U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+ ASSERT_EQ(0U, loc_regs.count(255));
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_register) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x300, 0x303, &loc_regs));
+ ASSERT_EQ(0x303U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(2);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+ ASSERT_EQ(1U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x09, 0xff, 0x01, 0xff, 0x03});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x4300, 0x4305, &loc_regs));
+ ASSERT_EQ(0x4305U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(255);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+ ASSERT_EQ(511U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_state) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x0a});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x300, 0x301, &loc_regs));
+ ASSERT_EQ(0x301U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x4300, std::vector<uint8_t>{0x0b});
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x4300, 0x4301, &loc_regs));
+ ASSERT_EQ(0x4301U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x85, 0x02, 0x0a, 0x86, 0x04, 0x0b});
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2005, &loc_regs));
+ ASSERT_EQ(0x2005U, this->dmem_->cur_offset());
+ ASSERT_EQ(2U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(6));
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2006, &loc_regs));
+ ASSERT_EQ(0x2006U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+
+ ResetLogs();
+ this->memory_.SetMemory(
+ 0x6000, std::vector<uint8_t>{0x0a, 0x85, 0x02, 0x0a, 0x86, 0x04, 0x0a, 0x87, 0x01, 0x0a, 0x89,
+ 0x05, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b});
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600c, &loc_regs));
+ ASSERT_EQ(0x600cU, this->dmem_->cur_offset());
+ ASSERT_EQ(4U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(6));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(7));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(9));
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600d, &loc_regs));
+ ASSERT_EQ(0x600dU, this->dmem_->cur_offset());
+ ASSERT_EQ(3U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(6));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(7));
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600e, &loc_regs));
+ ASSERT_EQ(0x600eU, this->dmem_->cur_offset());
+ ASSERT_EQ(2U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+ ASSERT_NE(loc_regs.end(), loc_regs.find(6));
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x600f, &loc_regs));
+ ASSERT_EQ(0x600fU, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_NE(loc_regs.end(), loc_regs.find(5));
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x6010, &loc_regs));
+ ASSERT_EQ(0x6010U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x6000, 0x6011, &loc_regs));
+ ASSERT_EQ(0x6011U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+}
+
+// This test verifies that the cfa offset is saved and restored properly.
+// Even though the spec is not clear about whether the offset is also
+// restored, the gcc unwinder does, and libunwind does too.
+TYPED_TEST_P(DwarfCfaTest, cfa_state_cfa_offset_restore) {
+ this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x0a, 0x0e, 0x40, 0x0b});
+ dwarf_loc_regs_t loc_regs;
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {5, 100}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x3000, 0x3004, &loc_regs));
+ ASSERT_EQ(0x3004U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(5U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(100U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0c, 0x7f, 0x74});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs));
+ ASSERT_EQ(0x103U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x7fU, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x74U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0c, 0xff, 0x02, 0xf4, 0x04});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x205, &loc_regs));
+ ASSERT_EQ(0x205U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x17fU, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x274U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x12, 0x30, 0x25});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs));
+ ASSERT_EQ(0x103U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x30U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x128U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Test a negative value.
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x12, 0xa3, 0x01, 0xfa, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x205, &loc_regs));
+ ASSERT_EQ(0x205U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0xa3U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(static_cast<uint64_t>(-48), loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_register) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0d, 0x72});
+ dwarf_loc_regs_t loc_regs;
+
+ // This fails because the cfa is not defined as a register.
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0U, loc_regs.size());
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+
+ ASSERT_EQ("4 unwind Attempt to set new register, but cfa is not already set to a register.\n",
+ GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3, 20}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0x102U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x72U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(20U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0d, 0xf9, 0x20});
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3, 60}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x203, &loc_regs));
+ ASSERT_EQ(0x203U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(0x1079U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(60U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_offset) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0e, 0x59});
+ dwarf_loc_regs_t loc_regs;
+
+ // This fails because the cfa is not defined as a register.
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0U, loc_regs.size());
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+
+ ASSERT_EQ("4 unwind Attempt to set offset, but cfa is not set to a register.\n",
+ GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0x102U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x59U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x0e, 0xd4, 0x0a});
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x203, &loc_regs));
+ ASSERT_EQ(0x203U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x554U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_offset_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x13, 0x23});
+ dwarf_loc_regs_t loc_regs;
+
+ // This fails because the cfa is not defined as a register.
+ ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->cfa_->last_error());
+
+ ASSERT_EQ("4 unwind Attempt to set offset, but cfa is not set to a register.\n",
+ GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x102, &loc_regs));
+ ASSERT_EQ(0x102U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(0x118U, loc_regs[CFA_REG].values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Negative offset.
+ ResetLogs();
+ this->memory_.SetMemory(0x200, std::vector<uint8_t>{0x13, 0xf6, 0x7f});
+ loc_regs.clear();
+ loc_regs[CFA_REG] = {.type = DWARF_LOCATION_REGISTER, .values = {3}};
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x203, &loc_regs));
+ ASSERT_EQ(0x203U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, loc_regs[CFA_REG].type);
+ ASSERT_EQ(3U, loc_regs[CFA_REG].values[0]);
+ ASSERT_EQ(static_cast<TypeParam>(-80), static_cast<TypeParam>(loc_regs[CFA_REG].values[1]));
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_def_cfa_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x0f, 0x04, 0x01, 0x02, 0x03, 0x04});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x106, &loc_regs));
+ ASSERT_EQ(0x106U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x0f, 0x81, 0x01};
+ for (uint8_t i = 3; i < 132; i++) {
+ ops.push_back(i - 1);
+ }
+ this->memory_.SetMemory(0x200, ops);
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x284, &loc_regs));
+ ASSERT_EQ(0x284U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x10, 0x04, 0x02, 0x40, 0x20});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x105, &loc_regs));
+ ASSERT_EQ(0x105U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(4);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_EXPRESSION, location->second.type);
+ ASSERT_EQ(2U, location->second.values[0]);
+ ASSERT_EQ(0x105U, location->second.values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x10, 0xff, 0x01, 0x82, 0x01};
+ for (uint8_t i = 5; i < 135; i++) {
+ ops.push_back(i - 4);
+ }
+
+ this->memory_.SetMemory(0x200, ops);
+ loc_regs.clear();
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x200, 0x287, &loc_regs));
+ ASSERT_EQ(0x287U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(255);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_EXPRESSION, location->second.type);
+ ASSERT_EQ(130U, location->second.values[0]);
+ ASSERT_EQ(0x287U, location->second.values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_val_offset) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x14, 0x45, 0x54});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs));
+ ASSERT_EQ(0x103U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(69);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type);
+ ASSERT_EQ(0x2a0U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x400, std::vector<uint8_t>{0x14, 0xa2, 0x02, 0xb4, 0x05});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x400, 0x405, &loc_regs));
+ ASSERT_EQ(0x405U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(290);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type);
+ ASSERT_EQ(0x15a0U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_val_offset_sf) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x15, 0x56, 0x12});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x103, &loc_regs));
+ ASSERT_EQ(0x103U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(86);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type);
+ ASSERT_EQ(0x90U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ // Negative value.
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0xa00, std::vector<uint8_t>{0x15, 0xff, 0x01, 0xc0, 0x7f});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0xa00, 0xa05, &loc_regs));
+ ASSERT_EQ(0xa05U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(255);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_OFFSET, location->second.type);
+ ASSERT_EQ(static_cast<uint64_t>(-512), location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_val_expression) {
+ this->memory_.SetMemory(0x100, std::vector<uint8_t>{0x16, 0x05, 0x02, 0x10, 0x20});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x100, 0x105, &loc_regs));
+ ASSERT_EQ(0x105U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(5);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_EXPRESSION, location->second.type);
+ ASSERT_EQ(2U, location->second.values[0]);
+ ASSERT_EQ(0x105U, location->second.values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ std::vector<uint8_t> ops{0x16, 0x83, 0x10, 0xa8, 0x01};
+ for (uint8_t i = 0; i < 168; i++) {
+ ops.push_back(i);
+ }
+
+ this->memory_.SetMemory(0xa00, ops);
+ loc_regs.clear();
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0xa00, 0xaad, &loc_regs));
+ ASSERT_EQ(0xaadU, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(2051);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_VAL_EXPRESSION, location->second.type);
+ ASSERT_EQ(168U, location->second.values[0]);
+ ASSERT_EQ(0xaadU, location->second.values[1]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_gnu_args_size) {
+ this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2e, 0x04});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2002, &loc_regs));
+ ASSERT_EQ(0x2002U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x2e, 0xa4, 0x80, 0x04});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x5000, 0x5004, &loc_regs));
+ ASSERT_EQ(0x5004U, this->dmem_->cur_offset());
+ ASSERT_EQ(0U, loc_regs.size());
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_gnu_negative_offset_extended) {
+ this->memory_.SetMemory(0x500, std::vector<uint8_t>{0x2f, 0x08, 0x10});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x500, 0x503, &loc_regs));
+ ASSERT_EQ(0x503U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(8);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(static_cast<uint64_t>(-16), location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+
+ ResetLogs();
+ loc_regs.clear();
+ this->memory_.SetMemory(0x1500, std::vector<uint8_t>{0x2f, 0x81, 0x02, 0xff, 0x01});
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x1500, 0x1505, &loc_regs));
+ ASSERT_EQ(0x1505U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ location = loc_regs.find(257);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_OFFSET, location->second.type);
+ ASSERT_EQ(static_cast<uint64_t>(-255), location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+TYPED_TEST_P(DwarfCfaTest, cfa_register_override) {
+ this->memory_.SetMemory(0x300, std::vector<uint8_t>{0x09, 0x02, 0x01, 0x09, 0x02, 0x04});
+ dwarf_loc_regs_t loc_regs;
+
+ ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x300, 0x306, &loc_regs));
+ ASSERT_EQ(0x306U, this->dmem_->cur_offset());
+ ASSERT_EQ(1U, loc_regs.size());
+ auto location = loc_regs.find(2);
+ ASSERT_NE(loc_regs.end(), location);
+ ASSERT_EQ(DWARF_LOCATION_REGISTER, location->second.type);
+ ASSERT_EQ(4U, location->second.values[0]);
+
+ ASSERT_EQ("", GetFakeLogPrint());
+ ASSERT_EQ("", GetFakeLogBuf());
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfCfaTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
+ cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
+ cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4, cfa_undefined,
+ cfa_same, cfa_register, cfa_state, cfa_state_cfa_offset_restore,
+ cfa_def_cfa, cfa_def_cfa_sf, cfa_def_cfa_register, cfa_def_cfa_offset,
+ cfa_def_cfa_offset_sf, cfa_def_cfa_expression, cfa_expression,
+ cfa_val_offset, cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
+ cfa_gnu_negative_offset_extended, cfa_register_override);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfCfaTest, DwarfCfaTestTypes);
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoTest.cpp
new file mode 100644
index 0000000..c846ad7
--- /dev/null
+++ b/libunwindstack/tests/MapInfoTest.cpp
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "Elf.h"
+#include "MapInfo.h"
+#include "Memory.h"
+
+class MapInfoTest : public ::testing::Test {
+ protected:
+ static void SetUpTestCase() {
+ std::vector<uint8_t> buffer(1024);
+ memcpy(buffer.data(), ELFMAG, SELFMAG);
+ for (size_t i = SELFMAG; i < buffer.size(); i++) {
+ buffer[i] = i / 256 + 1;
+ }
+ ASSERT_TRUE(android::base::WriteFully(elf_.fd, buffer.data(), buffer.size()));
+
+ for (size_t i = 0; i < 0x100; i++) {
+ buffer[i] = i / 256 + 1;
+ }
+ memcpy(&buffer[0x100], ELFMAG, SELFMAG);
+ for (size_t i = 0x100 + SELFMAG; i < buffer.size(); i++) {
+ buffer[i] = i / 256 + 1;
+ }
+ ASSERT_TRUE(android::base::WriteFully(elf_at_100_.fd, buffer.data(), buffer.size()));
+ }
+
+ static TemporaryFile elf_;
+
+ static TemporaryFile elf_at_100_;
+};
+TemporaryFile MapInfoTest::elf_;
+TemporaryFile MapInfoTest::elf_at_100_;
+
+TEST_F(MapInfoTest, end_le_start) {
+ MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path};
+
+ std::unique_ptr<Memory> memory;
+ memory.reset(info.CreateMemory(getpid()));
+ ASSERT_TRUE(memory.get() == nullptr);
+
+ info.end = 0xff;
+ memory.reset(info.CreateMemory(getpid()));
+ ASSERT_TRUE(memory.get() == nullptr);
+
+ // Make sure this test is valid.
+ info.end = 0x101;
+ memory.reset(info.CreateMemory(getpid()));
+ ASSERT_FALSE(info.CreateMemory(getpid()) == nullptr);
+}
+
+// Verify that if the offset is non-zero but there is no elf at the offset,
+// that the full file is used.
+TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_full_file) {
+ MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_.path};
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
+ ASSERT_TRUE(memory.get() != nullptr);
+ ASSERT_EQ(0x100U, info.elf_offset);
+
+ // Read the entire file.
+ std::vector<uint8_t> buffer(1024);
+ ASSERT_TRUE(memory->Read(0, buffer.data(), 1024));
+ ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
+ for (size_t i = SELFMAG; i < buffer.size(); i++) {
+ ASSERT_EQ(i / 256 + 1, buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_FALSE(memory->Read(1024, buffer.data(), 1));
+}
+
+// Verify that if the offset is non-zero and there is an elf at that
+// offset, that only part of the file is used.
+TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_partial_file) {
+ MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_at_100_.path};
+
+ std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
+ ASSERT_TRUE(memory.get() != nullptr);
+ ASSERT_EQ(0U, info.elf_offset);
+
+ // Read the valid part of the file.
+ std::vector<uint8_t> buffer(0x100);
+ ASSERT_TRUE(memory->Read(0, buffer.data(), 0x100));
+ ASSERT_TRUE(memcmp(buffer.data(), ELFMAG, SELFMAG) == 0);
+ for (size_t i = SELFMAG; i < buffer.size(); i++) {
+ ASSERT_EQ(2, buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_FALSE(memory->Read(0x100, buffer.data(), 1));
+}
+
+// Verify that device file names will never result in Memory object creation.
+TEST_F(MapInfoTest, create_memory_check_device_maps) {
+ // Set up some memory so that a valid local memory object would
+ // be returned if the file mapping fails, but the device check is incorrect.
+ std::vector<uint8_t> buffer(1024);
+ MapInfo info;
+ info.start = reinterpret_cast<uint64_t>(buffer.data());
+ info.end = info.start + buffer.size();
+ info.offset = 0;
+ std::unique_ptr<Memory> memory;
+
+ info.flags = 0x8000;
+ info.name = "/dev/something";
+ memory.reset(info.CreateMemory(getpid()));
+ ASSERT_TRUE(memory.get() == nullptr);
+}
+
+TEST_F(MapInfoTest, create_memory_local_memory) {
+ // Set up some memory for a valid local memory object.
+ std::vector<uint8_t> buffer(1024);
+ for (size_t i = 0; i < buffer.size(); i++) {
+ buffer[i] = i % 256;
+ }
+
+ MapInfo info;
+ info.start = reinterpret_cast<uint64_t>(buffer.data());
+ info.end = info.start + buffer.size();
+ info.offset = 0;
+
+ std::unique_ptr<Memory> memory;
+ memory.reset(info.CreateMemory(getpid()));
+ ASSERT_TRUE(memory.get() != nullptr);
+
+ std::vector<uint8_t> read_buffer(1024);
+ ASSERT_TRUE(memory->Read(0, read_buffer.data(), read_buffer.size()));
+ for (size_t i = 0; i < read_buffer.size(); i++) {
+ ASSERT_EQ(i % 256, read_buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_FALSE(memory->Read(read_buffer.size(), read_buffer.data(), 1));
+}
+
+TEST_F(MapInfoTest, create_memory_remote_memory) {
+ std::vector<uint8_t> buffer(1024);
+ memset(buffer.data(), 0xa, buffer.size());
+
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ while (true)
+ ;
+ exit(1);
+ }
+ ASSERT_LT(0, pid);
+
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) != -1);
+ uint64_t iterations = 0;
+ siginfo_t si;
+ while (TEMP_FAILURE_RETRY(ptrace(PTRACE_GETSIGINFO, pid, 0, &si)) < 0 && errno == ESRCH) {
+ usleep(30);
+ iterations++;
+ ASSERT_LT(iterations, 500000000ULL);
+ }
+
+ MapInfo info;
+ info.start = reinterpret_cast<uint64_t>(buffer.data());
+ info.end = info.start + buffer.size();
+ info.offset = 0;
+
+ std::unique_ptr<Memory> memory;
+ memory.reset(info.CreateMemory(pid));
+ ASSERT_TRUE(memory.get() != nullptr);
+ // Set the local memory to a different value to guarantee we are reading
+ // from the remote process.
+ memset(buffer.data(), 0x1, buffer.size());
+ std::vector<uint8_t> read_buffer(1024);
+ ASSERT_TRUE(memory->Read(0, read_buffer.data(), read_buffer.size()));
+ for (size_t i = 0; i < read_buffer.size(); i++) {
+ ASSERT_EQ(0xaU, read_buffer[i]) << "Failed at byte " << i;
+ }
+
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ kill(pid, SIGKILL);
+}
+
+TEST_F(MapInfoTest, get_elf) {
+ // Create a map to use as initialization data.
+ void* map = mmap(nullptr, 1024, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ ASSERT_NE(MAP_FAILED, map);
+
+ uint64_t start = reinterpret_cast<uint64_t>(map);
+ MapInfo info{.start = start, .end = start + 1024, .offset = 0, .name = ""};
+
+ // The map contains garbage, but this should still produce an elf object.
+ Elf* elf = info.GetElf(getpid(), false);
+ ASSERT_TRUE(elf != nullptr);
+ ASSERT_FALSE(elf->valid());
+
+ ASSERT_EQ(0, munmap(map, 1024));
+}
diff --git a/libusbhost/Android.bp b/libusbhost/Android.bp
new file mode 100644
index 0000000..a0d6b9b
--- /dev/null
+++ b/libusbhost/Android.bp
@@ -0,0 +1,35 @@
+//
+// Copyright (C) 2010 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library {
+ name: "libusbhost",
+ host_supported: true,
+ srcs: ["usbhost.c"],
+ cflags: ["-Werror"],
+ export_include_dirs: ["include"],
+ target: {
+ android: {
+ cflags: [
+ "-g",
+ "-DUSE_LIBLOG",
+ ],
+ shared_libs: ["liblog"],
+ },
+ darwin: {
+ enabled: false,
+ },
+ },
+}
diff --git a/libusbhost/Android.mk b/libusbhost/Android.mk
deleted file mode 100644
index 9439846..0000000
--- a/libusbhost/Android.mk
+++ /dev/null
@@ -1,56 +0,0 @@
-#
-# Copyright (C) 2010 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-# Static library for Linux host
-# ========================================================
-
-ifeq ($(HOST_OS),linux)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libusbhost
-LOCAL_SRC_FILES := usbhost.c
-LOCAL_CFLAGS := -Werror
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-endif
-
-# Shared library for target
-# ========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libusbhost
-LOCAL_SRC_FILES := usbhost.c
-LOCAL_CFLAGS := -g -DUSE_LIBLOG -Werror
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-# needed for logcat
-LOCAL_SHARED_LIBRARIES := libcutils
-include $(BUILD_SHARED_LIBRARY)
-
-# Static library for target
-# ========================================================
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := libusbhost
-LOCAL_SRC_FILES := usbhost.c
-LOCAL_CFLAGS := -Werror
-LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-include $(BUILD_STATIC_LIBRARY)
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index 68aca17..050fc2f 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -19,7 +19,7 @@
#ifdef USE_LIBLOG
#define LOG_TAG "usbhost"
-#include "utils/Log.h"
+#include "log/log.h"
#define D ALOGD
#else
#define D printf
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index e487a97..9c07472 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -1216,7 +1216,12 @@
signal(SIGALRM, caught_blocking_clear);
alarm(2);
while (fgets(buffer, sizeof(buffer), fp)) {
- if (!strncmp(buffer, "clearLog: ", 10)) {
+ if (!strncmp(buffer, "clearLog: ", strlen("clearLog: "))) {
+ fprintf(stderr, "WARNING: Test lacks permission to run :-(\n");
+ count = signals = 1;
+ break;
+ }
+ if (!strncmp(buffer, "failed to clear", strlen("failed to clear"))) {
fprintf(stderr, "WARNING: Test lacks permission to run :-(\n");
count = signals = 1;
break;
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
index c67d2bf..a9edc3e 100644
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -44,14 +44,14 @@
// LogTimeEntrys, and spawn a transitory per-client thread to
// work at filing data to the socket.
//
-// global LogTimeEntry::lock() is used to protect access,
+// global LogTimeEntry::wrlock() is used to protect access,
// reference counts are used to ensure that individual
// LogTimeEntry lifetime is managed when not protected.
void FlushCommand::runSocketCommand(SocketClient* client) {
LogTimeEntry* entry = NULL;
LastLogTimes& times = mReader.logbuf().mTimes;
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
LastLogTimes::iterator it = times.begin();
while (it != times.end()) {
entry = (*it);
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 2d9024a..ee38d1c 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -277,7 +277,7 @@
++cp;
}
tid = pid;
- logbuf->lock();
+ logbuf->wrlock();
uid = logbuf->pidToUid(pid);
logbuf->unlock();
memmove(pidptr, cp, strlen(cp) + 1);
@@ -322,7 +322,7 @@
pid = tid;
comm = "auditd";
} else {
- logbuf->lock();
+ logbuf->wrlock();
comm = commfree = logbuf->pidToName(pid);
logbuf->unlock();
if (!comm) {
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index a6bea0c..d9ec081 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -72,7 +72,7 @@
// as the act of mounting /data would trigger persist.logd.timestamp to
// be corrected. 1/30 corner case YMMV.
//
- pthread_mutex_lock(&mLogElementsLock);
+ rdlock();
LogBufferElementCollection::iterator it = mLogElements.begin();
while ((it != mLogElements.end())) {
LogBufferElement* e = *it;
@@ -87,7 +87,7 @@
}
++it;
}
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
}
// We may have been triggered by a SIGHUP. Release any sleeping reader
@@ -95,7 +95,7 @@
//
// NB: this is _not_ performed in the context of a SIGHUP, it is
// performed during startup, and in context of reinit administrative thread
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
@@ -111,7 +111,7 @@
LogBuffer::LogBuffer(LastLogTimes* times)
: monotonic(android_log_clockid() == CLOCK_MONOTONIC), mTimes(*times) {
- pthread_mutex_init(&mLogElementsLock, nullptr);
+ pthread_rwlock_init(&mLogElementsLock, nullptr);
log_id_for_each(i) {
lastLoggedElements[i] = nullptr;
@@ -207,16 +207,15 @@
}
if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
// Log traffic received to total
- pthread_mutex_lock(&mLogElementsLock);
- stats.add(elem);
- stats.subtract(elem);
- pthread_mutex_unlock(&mLogElementsLock);
+ wrlock();
+ stats.addTotal(elem);
+ unlock();
delete elem;
return -EACCES;
}
}
- pthread_mutex_lock(&mLogElementsLock);
+ wrlock();
LogBufferElement* currentLast = lastLoggedElements[log_id];
if (currentLast) {
LogBufferElement* dropped = droppedElements[log_id];
@@ -317,15 +316,14 @@
// check for overflow
if (total >= UINT32_MAX) {
log(currentLast);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return len;
}
- stats.add(currentLast);
- stats.subtract(currentLast);
+ stats.addTotal(currentLast);
delete currentLast;
swab = total;
event->payload.data = htole32(swab);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return len;
}
if (count == USHRT_MAX) {
@@ -337,13 +335,12 @@
}
}
if (count) {
- stats.add(currentLast);
- stats.subtract(currentLast);
+ stats.addTotal(currentLast);
currentLast->setDropped(count);
}
droppedElements[log_id] = currentLast;
lastLoggedElements[log_id] = elem;
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return len;
}
if (dropped) { // State 1 or 2
@@ -361,12 +358,12 @@
lastLoggedElements[log_id] = new LogBufferElement(*elem);
log(elem);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return len;
}
-// assumes mLogElementsLock held, owns elem, will look after garbage collection
+// assumes LogBuffer::wrlock() held, owns elem, look after garbage collection
void LogBuffer::log(LogBufferElement* elem) {
// cap on how far back we will sort in-place, otherwise append
static uint32_t too_far_back = 5; // five seconds
@@ -387,7 +384,7 @@
bool end_set = false;
bool end_always = false;
- LogTimeEntry::lock();
+ LogTimeEntry::rdlock();
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
@@ -429,7 +426,7 @@
// Prune at most 10% of the log entries or maxPrune, whichever is less.
//
-// mLogElementsLock must be held when this function is called.
+// LogBuffer::wrlock() must be held when this function is called.
void LogBuffer::maybePrune(log_id_t id) {
size_t sizes = stats.sizes(id);
unsigned long maxSize = log_buffer_size(id);
@@ -653,14 +650,14 @@
// The third thread is optional, and only gets hit if there was a whitelist
// and more needs to be pruned against the backstop of the region lock.
//
-// mLogElementsLock must be held when this function is called.
+// LogBuffer::wrlock() must be held when this function is called.
//
bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
LogTimeEntry* oldest = nullptr;
bool busy = false;
bool clearAll = pruneRows == ULONG_MAX;
- LogTimeEntry::lock();
+ LogTimeEntry::rdlock();
// Region locked?
LastLogTimes::iterator times = mTimes.begin();
@@ -1020,15 +1017,15 @@
// one entry, not another clear run, so we are looking for
// the quick side effect of the return value to tell us if
// we have a _blocked_ reader.
- pthread_mutex_lock(&mLogElementsLock);
+ wrlock();
busy = prune(id, 1, uid);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
// It is still busy, blocked reader(s), lets kill them all!
// otherwise, lets be a good citizen and preserve the slow
// readers and let the clear run (below) deal with determining
// if we are still blocked and return an error code to caller.
if (busy) {
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
LastLogTimes::iterator times = mTimes.begin();
while (times != mTimes.end()) {
LogTimeEntry* entry = (*times);
@@ -1041,9 +1038,9 @@
LogTimeEntry::unlock();
}
}
- pthread_mutex_lock(&mLogElementsLock);
+ wrlock();
busy = prune(id, ULONG_MAX, uid);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
if (!busy || !--retry) {
break;
}
@@ -1054,9 +1051,9 @@
// get the used space associated with "id".
unsigned long LogBuffer::getSizeUsed(log_id_t id) {
- pthread_mutex_lock(&mLogElementsLock);
+ rdlock();
size_t retval = stats.sizes(id);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return retval;
}
@@ -1066,17 +1063,17 @@
if (!__android_logger_valid_buffer_size(size)) {
return -1;
}
- pthread_mutex_lock(&mLogElementsLock);
+ wrlock();
log_buffer_size(id) = size;
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return 0;
}
// get the total space allocated to "id"
unsigned long LogBuffer::getSize(log_id_t id) {
- pthread_mutex_lock(&mLogElementsLock);
+ rdlock();
size_t retval = log_buffer_size(id);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return retval;
}
@@ -1088,7 +1085,7 @@
LogBufferElementCollection::iterator it;
uid_t uid = reader->getUid();
- pthread_mutex_lock(&mLogElementsLock);
+ rdlock();
if (start == log_time::EPOCH) {
// client wants to start from the beginning
@@ -1146,7 +1143,7 @@
continue;
}
- // NB: calling out to another object with mLogElementsLock held (safe)
+ // NB: calling out to another object with wrlock() held (safe)
if (filter) {
int ret = (*filter)(element, arg);
if (ret == false) {
@@ -1169,7 +1166,7 @@
(element->getDropped() && !sameTid) ? 0 : element->getTid();
}
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
// range locking in LastLogTimes looks after us
max = element->flushTo(reader, this, privileged, sameTid);
@@ -1179,20 +1176,20 @@
}
skip = maxSkip;
- pthread_mutex_lock(&mLogElementsLock);
+ rdlock();
}
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return max;
}
std::string LogBuffer::formatStatistics(uid_t uid, pid_t pid,
unsigned int logMask) {
- pthread_mutex_lock(&mLogElementsLock);
+ wrlock();
std::string ret = stats.format(uid, pid, logMask);
- pthread_mutex_unlock(&mLogElementsLock);
+ unlock();
return ret;
}
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 1272c20..51edd86 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -76,7 +76,7 @@
class LogBuffer {
LogBufferElementCollection mLogElements;
- pthread_mutex_t mLogElementsLock;
+ pthread_rwlock_t mLogElementsLock;
LogStatistics stats;
@@ -154,7 +154,7 @@
return tags.tagToName(tag);
}
- // helper must be protected directly or implicitly by lock()/unlock()
+ // helper must be protected directly or implicitly by wrlock()/unlock()
const char* pidToName(pid_t pid) {
return stats.pidToName(pid);
}
@@ -164,11 +164,14 @@
const char* uidToName(uid_t uid) {
return stats.uidToName(uid);
}
- void lock() {
- pthread_mutex_lock(&mLogElementsLock);
+ void wrlock() {
+ pthread_rwlock_wrlock(&mLogElementsLock);
+ }
+ void rdlock() {
+ pthread_rwlock_rdlock(&mLogElementsLock);
}
void unlock() {
- pthread_mutex_unlock(&mLogElementsLock);
+ pthread_rwlock_unlock(&mLogElementsLock);
}
private:
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 04a620c..381c974 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -121,7 +121,7 @@
}
static const char format_uid[] = "uid=%u%s%s %s %u line%s";
- parent->lock();
+ parent->wrlock();
const char* name = parent->uidToName(mUid);
parent->unlock();
const char* commName = android::tidToName(mTid);
@@ -129,7 +129,7 @@
commName = android::tidToName(mPid);
}
if (!commName) {
- parent->lock();
+ parent->wrlock();
commName = parent->pidToName(mPid);
parent->unlock();
}
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index d23254d..a7e7208 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -579,7 +579,7 @@
const pid_t tid = pid;
uid_t uid = AID_ROOT;
if (pid) {
- logbuf->lock();
+ logbuf->wrlock();
uid = logbuf->pidToUid(pid);
logbuf->unlock();
}
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index feb105f..6d69316 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -111,7 +111,7 @@
if (!fastcmp<strncmp>(buffer, "dumpAndClose", 12)) {
// Allow writer to get some cycles, and wait for pending notifications
sched_yield();
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
LogTimeEntry::unlock();
sched_yield();
nonBlock = true;
@@ -212,7 +212,7 @@
void LogReader::doSocketDelete(SocketClient* cli) {
LastLogTimes& times = mLogbuf.mTimes;
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
LastLogTimes::iterator it = times.begin();
while (it != times.end()) {
LogTimeEntry* entry = (*it);
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index d3167ad..d20d90e 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -15,6 +15,7 @@
*/
#include <fcntl.h>
+#include <inttypes.h>
#include <pwd.h>
#include <stdio.h>
#include <string.h>
@@ -23,19 +24,26 @@
#include <list>
-#include <android/log.h>
+#include <private/android_logger.h>
#include "LogStatistics.h"
+static const uint64_t hourSec = 60 * 60;
+static const uint64_t monthSec = 31 * 24 * hourSec;
+
size_t LogStatistics::SizesTotal;
LogStatistics::LogStatistics() : enable(false) {
+ log_time now(CLOCK_REALTIME);
log_id_for_each(id) {
mSizes[id] = 0;
mElements[id] = 0;
mDroppedElements[id] = 0;
mSizesTotal[id] = 0;
mElementsTotal[id] = 0;
+ mOldest[id] = now;
+ mNewest[id] = now;
+ mNewestDropped[id] = now;
}
}
@@ -70,25 +78,56 @@
}
}
+void LogStatistics::addTotal(LogBufferElement* element) {
+ if (element->getDropped()) return;
+
+ log_id_t log_id = element->getLogId();
+ unsigned short size = element->getMsgLen();
+ mSizesTotal[log_id] += size;
+ SizesTotal += size;
+ ++mElementsTotal[log_id];
+}
+
void LogStatistics::add(LogBufferElement* element) {
log_id_t log_id = element->getLogId();
unsigned short size = element->getMsgLen();
mSizes[log_id] += size;
++mElements[log_id];
+ // When caller adding a chatty entry, they will have already
+ // called add() and subtract() for each entry as they are
+ // evaluated and trimmed, thus recording size and number of
+ // elements, but we must recognize the manufactured dropped
+ // entry as not contributing to the lifetime totals.
if (element->getDropped()) {
++mDroppedElements[log_id];
} else {
- // When caller adding a chatty entry, they will have already
- // called add() and subtract() for each entry as they are
- // evaluated and trimmed, thus recording size and number of
- // elements, but we must recognize the manufactured dropped
- // entry as not contributing to the lifetime totals.
mSizesTotal[log_id] += size;
SizesTotal += size;
++mElementsTotal[log_id];
}
+ log_time stamp(element->getRealTime());
+ if (mNewest[log_id] < stamp) {
+ // A major time update invalidates the statistics :-(
+ log_time diff = stamp - mNewest[log_id];
+ mNewest[log_id] = stamp;
+
+ if (diff.tv_sec > hourSec) {
+ // approximate Do-Your-Best fixup
+ diff += mOldest[log_id];
+ if ((diff > stamp) && ((diff - stamp).tv_sec < hourSec)) {
+ diff = stamp;
+ }
+ if (diff <= stamp) {
+ mOldest[log_id] = diff;
+ if (mNewestDropped[log_id] < diff) {
+ mNewestDropped[log_id] = diff;
+ }
+ }
+ }
+ }
+
if (log_id == LOG_ID_KERNEL) {
return;
}
@@ -113,6 +152,10 @@
tagTable.add(tag, element);
}
}
+
+ if (!element->getDropped()) {
+ tagNameTable.add(TagNameKey(element), element);
+ }
}
void LogStatistics::subtract(LogBufferElement* element) {
@@ -124,6 +167,10 @@
--mDroppedElements[log_id];
}
+ if (mOldest[log_id] < element->getRealTime()) {
+ mOldest[log_id] = element->getRealTime();
+ }
+
if (log_id == LOG_ID_KERNEL) {
return;
}
@@ -148,6 +195,10 @@
tagTable.subtract(tag, element);
}
}
+
+ if (!element->getDropped()) {
+ tagNameTable.subtract(TagNameKey(element), element);
+ }
}
// Atomically set an entry to drop
@@ -158,6 +209,10 @@
mSizes[log_id] -= size;
++mDroppedElements[log_id];
+ if (mNewestDropped[log_id] < element->getRealTime()) {
+ mNewestDropped[log_id] = element->getRealTime();
+ }
+
uidTable[log_id].drop(element->getUid(), element);
if (element->getUid() == AID_SYSTEM) {
pidSystemTable[log_id].drop(element->getPid(), element);
@@ -178,9 +233,12 @@
tagTable.drop(tag, element);
}
}
+
+ tagNameTable.subtract(TagNameKey(element), element);
}
// caller must own and free character string
+// Requires parent LogBuffer::wrlock() to be held
const char* LogStatistics::uidToName(uid_t uid) const {
// Local hard coded favourites
if (uid == AID_LOGD) {
@@ -457,6 +515,101 @@
return formatLine(name, size, pruned);
}
+std::string TagNameEntry::formatHeader(const std::string& name,
+ log_id_t /* id */) const {
+ return formatLine(name, std::string("Size"), std::string("")) +
+ formatLine(std::string(" TID/PID/UID LOG_TAG NAME"),
+ std::string("BYTES"), std::string(""));
+}
+
+std::string TagNameEntry::format(const LogStatistics& /* stat */,
+ log_id_t /* id */) const {
+ std::string name;
+ pid_t tid = getTid();
+ pid_t pid = getPid();
+ std::string pidstr;
+ if (pid != (pid_t)-1) {
+ pidstr = android::base::StringPrintf("%u", pid);
+ if ((tid != (pid_t)-1) && (tid != pid)) pidstr = "/" + pidstr;
+ }
+ int len = 9 - pidstr.length();
+ if (len < 0) len = 0;
+ if ((tid == (pid_t)-1) || (tid == pid)) {
+ name = android::base::StringPrintf("%*s", len, "");
+ } else {
+ name = android::base::StringPrintf("%*u", len, tid);
+ }
+ name += pidstr;
+ uid_t uid = getUid();
+ if (uid != (uid_t)-1) {
+ name += android::base::StringPrintf("/%u", uid);
+ }
+
+ std::string size = android::base::StringPrintf("%zu", getSizes());
+
+ const char* nameTmp = getName();
+ if (nameTmp) {
+ size_t lenSpace = std::max(16 - name.length(), (size_t)1);
+ size_t len = EntryBaseConstants::total_len -
+ EntryBaseConstants::pruned_len - size.length() -
+ name.length() - lenSpace - 2;
+ size_t lenNameTmp = strlen(nameTmp);
+ while ((len < lenNameTmp) && (lenSpace > 1)) {
+ ++len;
+ --lenSpace;
+ }
+ name += android::base::StringPrintf("%*s", (int)lenSpace, "");
+ if (len < lenNameTmp) {
+ name += "...";
+ nameTmp += lenNameTmp - std::max(len - 3, (size_t)1);
+ }
+ name += nameTmp;
+ }
+
+ std::string pruned = "";
+
+ return formatLine(name, size, pruned);
+}
+
+static std::string formatMsec(uint64_t val) {
+ static const unsigned subsecDigits = 3;
+ static const uint64_t sec = MS_PER_SEC;
+
+ static const uint64_t minute = 60 * sec;
+ static const uint64_t hour = 60 * minute;
+ static const uint64_t day = 24 * hour;
+
+ std::string output;
+ if (val < sec) return output;
+
+ if (val >= day) {
+ output = android::base::StringPrintf("%" PRIu64 "d ", val / day);
+ val = (val % day) + day;
+ }
+ if (val >= minute) {
+ if (val >= hour) {
+ output += android::base::StringPrintf("%" PRIu64 ":",
+ (val / hour) % (day / hour));
+ }
+ output += android::base::StringPrintf(
+ (val >= hour) ? "%02" PRIu64 ":" : "%" PRIu64 ":",
+ (val / minute) % (hour / minute));
+ }
+ output +=
+ android::base::StringPrintf((val >= minute) ? "%02" PRIu64 : "%" PRIu64,
+ (val / sec) % (minute / sec));
+ val %= sec;
+ unsigned digits = subsecDigits;
+ while (digits && ((val % 10) == 0)) {
+ val /= 10;
+ --digits;
+ }
+ if (digits) {
+ output += android::base::StringPrintf(".%0*" PRIu64, digits, val);
+ }
+ return output;
+}
+
std::string LogStatistics::format(uid_t uid, pid_t pid,
unsigned int logMask) const {
static const unsigned short spaces_total = 19;
@@ -526,6 +679,67 @@
output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", totalSize,
totalEls);
+ static const char SpanStr[] = "\nLogspan";
+ spaces = 10 - strlen(SpanStr);
+ output += SpanStr;
+
+ // Total reports the greater of the individual maximum time span, or the
+ // validated minimum start and maximum end time span if it makes sense.
+ uint64_t minTime = UINT64_MAX;
+ uint64_t maxTime = 0;
+ uint64_t maxSpan = 0;
+ totalSize = 0;
+
+ log_id_for_each(id) {
+ if (!(logMask & (1 << id))) continue;
+
+ // validity checking
+ uint64_t oldest = mOldest[id].msec();
+ uint64_t newest = mNewest[id].msec();
+ if (newest <= oldest) {
+ spaces += spaces_total;
+ continue;
+ }
+
+ uint64_t span = newest - oldest;
+ if (span > (monthSec * MS_PER_SEC)) {
+ spaces += spaces_total;
+ continue;
+ }
+
+ // total span
+ if (minTime > oldest) minTime = oldest;
+ if (maxTime < newest) maxTime = newest;
+ if (span > maxSpan) maxSpan = span;
+ totalSize += span;
+
+ uint64_t dropped = mNewestDropped[id].msec();
+ if (dropped < oldest) dropped = oldest;
+ if (dropped > newest) dropped = newest;
+
+ oldLength = output.length();
+ output += android::base::StringPrintf("%*s%s", spaces, "",
+ formatMsec(span).c_str());
+ unsigned permille = ((newest - dropped) * 1000 + (span / 2)) / span;
+ if ((permille > 1) && (permille < 999)) {
+ output += android::base::StringPrintf("(%u", permille / 10);
+ permille %= 10;
+ if (permille) {
+ output += android::base::StringPrintf(".%u", permille);
+ }
+ output += android::base::StringPrintf("%%)");
+ }
+ spaces -= output.length() - oldLength;
+ spaces += spaces_total;
+ }
+ if ((maxTime > minTime) && ((maxTime -= minTime) < totalSize) &&
+ (maxTime > maxSpan)) {
+ maxSpan = maxTime;
+ }
+ if (spaces < 0) spaces = 0;
+ output += android::base::StringPrintf("%*s%s", spaces, "",
+ formatMsec(maxSpan).c_str());
+
static const char OverheadStr[] = "\nOverhead";
spaces = 10 - strlen(OverheadStr);
output += OverheadStr;
@@ -592,6 +806,13 @@
securityTagTable.format(*this, uid, pid, name, LOG_ID_SECURITY);
}
+ if (enable) {
+ name = "Chattiest TAGs";
+ if (pid) name += android::base::StringPrintf(" for PID %d", pid);
+ name += ":";
+ output += tagNameTable.format(*this, uid, pid, name);
+ }
+
return output;
}
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 066b7de..945fc0a 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -18,17 +18,23 @@
#define _LOGD_LOG_STATISTICS_H__
#include <ctype.h>
+#include <inttypes.h>
+#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/types.h>
#include <algorithm> // std::max
+#include <experimental/string_view>
#include <memory>
#include <string> // std::string
#include <unordered_map>
#include <android-base/stringprintf.h>
#include <android/log.h>
+#include <log/log_time.h>
#include <private/android_filesystem_config.h>
+#include <utils/FastStrcmp.h>
#include "LogBufferElement.h"
#include "LogUtils.h"
@@ -76,7 +82,7 @@
std::unique_ptr<const TEntry* []> sort(uid_t uid, pid_t pid,
size_t len) const {
if (!len) {
- std::unique_ptr<const TEntry* []> sorted(NULL);
+ std::unique_ptr<const TEntry* []> sorted(nullptr);
return sorted;
}
@@ -111,7 +117,7 @@
return sorted;
}
- inline iterator add(TKey key, LogBufferElement* element) {
+ inline iterator add(const TKey& key, const LogBufferElement* element) {
iterator it = map.find(key);
if (it == map.end()) {
it = map.insert(std::make_pair(key, TEntry(element))).first;
@@ -131,14 +137,21 @@
return it;
}
- void subtract(TKey key, LogBufferElement* element) {
+ void subtract(TKey&& key, const LogBufferElement* element) {
+ iterator it = map.find(std::move(key));
+ if ((it != map.end()) && it->second.subtract(element)) {
+ map.erase(it);
+ }
+ }
+
+ void subtract(const TKey& key, const LogBufferElement* element) {
iterator it = map.find(key);
if ((it != map.end()) && it->second.subtract(element)) {
map.erase(it);
}
}
- inline void drop(TKey key, LogBufferElement* element) {
+ inline void drop(TKey key, const LogBufferElement* element) {
iterator it = map.find(key);
if (it != map.end()) {
it->second.drop(element);
@@ -198,17 +211,18 @@
EntryBase() : size(0) {
}
- explicit EntryBase(LogBufferElement* element) : size(element->getMsgLen()) {
+ explicit EntryBase(const LogBufferElement* element)
+ : size(element->getMsgLen()) {
}
size_t getSizes() const {
return size;
}
- inline void add(LogBufferElement* element) {
+ inline void add(const LogBufferElement* element) {
size += element->getMsgLen();
}
- inline bool subtract(LogBufferElement* element) {
+ inline bool subtract(const LogBufferElement* element) {
size -= element->getMsgLen();
return !size;
}
@@ -239,7 +253,7 @@
EntryBaseDropped() : dropped(0) {
}
- explicit EntryBaseDropped(LogBufferElement* element)
+ explicit EntryBaseDropped(const LogBufferElement* element)
: EntryBase(element), dropped(element->getDropped()) {
}
@@ -247,15 +261,15 @@
return dropped;
}
- inline void add(LogBufferElement* element) {
+ inline void add(const LogBufferElement* element) {
dropped += element->getDropped();
EntryBase::add(element);
}
- inline bool subtract(LogBufferElement* element) {
+ inline bool subtract(const LogBufferElement* element) {
dropped -= element->getDropped();
return EntryBase::subtract(element) && !dropped;
}
- inline void drop(LogBufferElement* element) {
+ inline void drop(const LogBufferElement* element) {
dropped += 1;
EntryBase::subtract(element);
}
@@ -265,7 +279,7 @@
const uid_t uid;
pid_t pid;
- explicit UidEntry(LogBufferElement* element)
+ explicit UidEntry(const LogBufferElement* element)
: EntryBaseDropped(element),
uid(element->getUid()),
pid(element->getPid()) {
@@ -281,7 +295,7 @@
return pid;
}
- inline void add(LogBufferElement* element) {
+ inline void add(const LogBufferElement* element) {
if (pid != element->getPid()) {
pid = -1;
}
@@ -307,7 +321,7 @@
uid(android::pidToUid(pid)),
name(android::pidToName(pid)) {
}
- explicit PidEntry(LogBufferElement* element)
+ explicit PidEntry(const LogBufferElement* element)
: EntryBaseDropped(element),
pid(element->getPid()),
uid(element->getUid()),
@@ -317,7 +331,7 @@
: EntryBaseDropped(element),
pid(element.pid),
uid(element.uid),
- name(element.name ? strdup(element.name) : NULL) {
+ name(element.name ? strdup(element.name) : nullptr) {
}
~PidEntry() {
free(name);
@@ -339,14 +353,14 @@
inline void add(pid_t newPid) {
if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
free(name);
- name = NULL;
+ name = nullptr;
}
if (!name) {
name = android::pidToName(newPid);
}
}
- inline void add(LogBufferElement* element) {
+ inline void add(const LogBufferElement* element) {
uid_t incomingUid = element->getUid();
if (getUid() != incomingUid) {
uid = incomingUid;
@@ -375,7 +389,7 @@
uid(android::pidToUid(tid)),
name(android::tidToName(tid)) {
}
- explicit TidEntry(LogBufferElement* element)
+ explicit TidEntry(const LogBufferElement* element)
: EntryBaseDropped(element),
tid(element->getTid()),
pid(element->getPid()),
@@ -387,7 +401,7 @@
tid(element.tid),
pid(element.pid),
uid(element.uid),
- name(element.name ? strdup(element.name) : NULL) {
+ name(element.name ? strdup(element.name) : nullptr) {
}
~TidEntry() {
free(name);
@@ -412,14 +426,14 @@
inline void add(pid_t incomingTid) {
if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
free(name);
- name = NULL;
+ name = nullptr;
}
if (!name) {
name = android::tidToName(incomingTid);
}
}
- inline void add(LogBufferElement* element) {
+ inline void add(const LogBufferElement* element) {
uid_t incomingUid = element->getUid();
pid_t incomingPid = element->getPid();
if ((getUid() != incomingUid) || (getPid() != incomingPid)) {
@@ -442,7 +456,7 @@
pid_t pid;
uid_t uid;
- explicit TagEntry(LogBufferElement* element)
+ explicit TagEntry(const LogBufferElement* element)
: EntryBaseDropped(element),
tag(element->getTag()),
pid(element->getPid()),
@@ -462,7 +476,7 @@
return android::tagToName(tag);
}
- inline void add(LogBufferElement* element) {
+ inline void add(const LogBufferElement* element) {
if (uid != element->getUid()) {
uid = -1;
}
@@ -476,6 +490,144 @@
std::string format(const LogStatistics& stat, log_id_t id) const;
};
+struct TagNameKey {
+ std::string* alloc;
+ std::experimental::string_view name; // Saves space if const char*
+
+ explicit TagNameKey(const LogBufferElement* element)
+ : alloc(nullptr), name("", strlen("")) {
+ if (element->isBinary()) {
+ uint32_t tag = element->getTag();
+ if (tag) {
+ const char* cp = android::tagToName(tag);
+ if (cp) {
+ name = std::experimental::string_view(cp, strlen(cp));
+ return;
+ }
+ }
+ alloc = new std::string(
+ android::base::StringPrintf("[%" PRIu32 "]", tag));
+ if (!alloc) return;
+ name = std::experimental::string_view(alloc->c_str(), alloc->size());
+ return;
+ }
+ const char* msg = element->getMsg();
+ if (!msg) {
+ name = std::experimental::string_view("chatty", strlen("chatty"));
+ return;
+ }
+ ++msg;
+ unsigned short len = element->getMsgLen();
+ len = (len <= 1) ? 0 : strnlen(msg, len - 1);
+ if (!len) {
+ name = std::experimental::string_view("<NULL>", strlen("<NULL>"));
+ return;
+ }
+ alloc = new std::string(msg, len);
+ if (!alloc) return;
+ name = std::experimental::string_view(alloc->c_str(), alloc->size());
+ }
+
+ explicit TagNameKey(TagNameKey&& rval)
+ : alloc(rval.alloc), name(rval.name.data(), rval.name.length()) {
+ rval.alloc = nullptr;
+ }
+
+ explicit TagNameKey(const TagNameKey& rval)
+ : alloc(rval.alloc ? new std::string(*rval.alloc) : nullptr),
+ name(alloc ? alloc->data() : rval.name.data(), rval.name.length()) {
+ }
+
+ ~TagNameKey() {
+ if (alloc) delete alloc;
+ }
+
+ operator const std::experimental::string_view() const {
+ return name;
+ }
+
+ const char* data() const {
+ return name.data();
+ }
+ size_t length() const {
+ return name.length();
+ }
+
+ bool operator==(const TagNameKey& rval) const {
+ if (length() != rval.length()) return false;
+ if (length() == 0) return true;
+ return fastcmp<strncmp>(data(), rval.data(), length()) == 0;
+ }
+ bool operator!=(const TagNameKey& rval) const {
+ return !(*this == rval);
+ }
+
+ size_t getAllocLength() const {
+ return alloc ? alloc->length() + 1 + sizeof(std::string) : 0;
+ }
+};
+
+// Hash for TagNameKey
+template <>
+struct std::hash<TagNameKey>
+ : public std::unary_function<const TagNameKey&, size_t> {
+ size_t operator()(const TagNameKey& __t) const noexcept {
+ if (!__t.length()) return 0;
+ return std::hash<std::experimental::string_view>()(
+ std::experimental::string_view(__t));
+ }
+};
+
+struct TagNameEntry : public EntryBase {
+ pid_t tid;
+ pid_t pid;
+ uid_t uid;
+ TagNameKey name;
+
+ explicit TagNameEntry(const LogBufferElement* element)
+ : EntryBase(element),
+ tid(element->getTid()),
+ pid(element->getPid()),
+ uid(element->getUid()),
+ name(element) {
+ }
+
+ const TagNameKey& getKey() const {
+ return name;
+ }
+ const pid_t& getTid() const {
+ return tid;
+ }
+ const pid_t& getPid() const {
+ return pid;
+ }
+ const uid_t& getUid() const {
+ return uid;
+ }
+ const char* getName() const {
+ return name.data();
+ }
+ size_t getNameAllocLength() const {
+ return name.getAllocLength();
+ }
+
+ inline void add(const LogBufferElement* element) {
+ if (uid != element->getUid()) {
+ uid = -1;
+ }
+ if (pid != element->getPid()) {
+ pid = -1;
+ }
+ if (tid != element->getTid()) {
+ tid = -1;
+ }
+ EntryBase::add(element);
+ }
+
+ std::string formatHeader(const std::string& name, log_id_t id) const;
+ std::string format(const LogStatistics& stat, log_id_t id) const;
+};
+
template <typename TEntry>
class LogFindWorst {
std::unique_ptr<const TEntry* []> sorted;
@@ -520,6 +672,9 @@
size_t mDroppedElements[LOG_ID_MAX];
size_t mSizesTotal[LOG_ID_MAX];
size_t mElementsTotal[LOG_ID_MAX];
+ log_time mOldest[LOG_ID_MAX];
+ log_time mNewest[LOG_ID_MAX];
+ log_time mNewestDropped[LOG_ID_MAX];
static size_t SizesTotal;
bool enable;
@@ -546,9 +701,14 @@
// security tag list
tagTable_t securityTagTable;
+ // global tag list
+ typedef LogHashtable<TagNameKey, TagNameEntry> tagNameTable_t;
+ tagNameTable_t tagNameTable;
+
size_t sizeOf() const {
size_t size = sizeof(*this) + pidTable.sizeOf() + tidTable.sizeOf() +
tagTable.sizeOf() + securityTagTable.sizeOf() +
+ tagNameTable.sizeOf() +
(pidTable.size() * sizeof(pidTable_t::iterator)) +
(tagTable.size() * sizeof(tagTable_t::iterator));
for (auto it : pidTable) {
@@ -559,6 +719,7 @@
const char* name = it.second.getName();
if (name) size += strlen(name) + 1;
}
+ for (auto it : tagNameTable) size += it.second.getNameAllocLength();
log_id_for_each(id) {
size += uidTable[id].sizeOf();
size += uidTable[id].size() * sizeof(uidTable_t::iterator);
@@ -576,6 +737,7 @@
enable = true;
}
+ void addTotal(LogBufferElement* entry);
void add(LogBufferElement* entry);
void subtract(LogBufferElement* entry);
// entry->setDropped(1) must follow this call
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index ccc550a..25c2ad2 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -78,7 +78,7 @@
void LogTimeEntry::threadStop(void* obj) {
LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
- lock();
+ wrlock();
if (me->mNonBlock) {
me->error_Locked();
@@ -134,7 +134,7 @@
me->leadingDropped = true;
- lock();
+ wrlock();
log_time start = me->mStart;
@@ -160,7 +160,7 @@
start = logbuf.flushTo(client, start, me->mLastTid, privileged,
security, FilterSecondPass, me);
- lock();
+ wrlock();
if (start == LogBufferElement::FLUSH_ERROR) {
me->error_Locked();
@@ -191,7 +191,7 @@
int LogTimeEntry::FilterFirstPass(const LogBufferElement* element, void* obj) {
LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
if (me->leadingDropped) {
if (element->getDropped()) {
@@ -219,7 +219,7 @@
int LogTimeEntry::FilterSecondPass(const LogBufferElement* element, void* obj) {
LogTimeEntry* me = reinterpret_cast<LogTimeEntry*>(obj);
- LogTimeEntry::lock();
+ LogTimeEntry::wrlock();
me->mStart = element->getRealTime();
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index ec8252e..9ca2aea 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -61,7 +61,10 @@
const log_time mEnd; // only relevant if mNonBlock
// Protect List manipulations
- static void lock(void) {
+ static void wrlock(void) {
+ pthread_mutex_lock(×Lock);
+ }
+ static void rdlock(void) {
pthread_mutex_lock(×Lock);
}
static void unlock(void) {
@@ -104,7 +107,7 @@
mError = true;
}
void error(void) {
- lock();
+ wrlock();
error_Locked();
unlock();
}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index a94a717..2dd5771 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -495,12 +495,28 @@
# Set indication (checked by vold) that we have finished this action
#setprop vold.post_fs_data_done 1
-# This trigger will be triggered before 'zygote-start' since there is no zygote-start defined in
-# current init.rc. It is recommended to put unnecessary data/ initialization from post-fs-data
-# to start-zygote to unblock zygote start.
+# It is recommended to put unnecessary data/ initialization from post-fs-data
+# to start-zygote in device's init.rc to unblock zygote start.
+on zygote-start && property:ro.crypto.state=unencrypted
+ # A/B update verifier that marks a successful boot.
+ exec_start update_verifier_nonencrypted
+ start netd
+ start zygote
+ start zygote_secondary
+
+on zygote-start && property:ro.crypto.state=unsupported
+ # A/B update verifier that marks a successful boot.
+ exec_start update_verifier_nonencrypted
+ start netd
+ start zygote
+ start zygote_secondary
+
on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
- start netd
- start zygote
+ # A/B update verifier that marks a successful boot.
+ exec_start update_verifier_nonencrypted
+ start netd
+ start zygote
+ start zygote_secondary
on boot
# basic network init
@@ -599,8 +615,6 @@
class_start core
on nonencrypted
- # A/B update verifier that marks a successful boot.
- exec_start update_verifier_nonencrypted
class_start main
class_start late_start