Merge "Update BacktraceOffline for LLVM rebase to r256229."
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index db9b710..d29c08e 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -50,6 +50,15 @@
__adb_serial = serial;
}
+void adb_get_transport(TransportType* type, const char** serial) {
+ if (type) {
+ *type = __adb_transport;
+ }
+ if (serial) {
+ *serial = __adb_serial;
+ }
+}
+
void adb_set_tcp_specifics(int server_port)
{
__adb_server_port = server_port;
diff --git a/adb/adb_client.h b/adb/adb_client.h
index a9df4d7..d5cd922 100644
--- a/adb/adb_client.h
+++ b/adb/adb_client.h
@@ -39,6 +39,9 @@
// Set the preferred transport to connect to.
void adb_set_transport(TransportType type, const char* _Nullable serial);
+// Get the preferred transport to connect to.
+void adb_get_transport(TransportType* _Nullable type, const char* _Nullable* _Nullable serial);
+
// Set TCP specifics of the transport to use.
void adb_set_tcp_specifics(int server_port);
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index f1149b3..89fcd66 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -19,6 +19,8 @@
#include <string>
+#include <android-base/macros.h>
+
void close_stdin();
bool getcwd(std::string* cwd);
@@ -39,4 +41,45 @@
bool set_file_block_mode(int fd, bool block);
+extern int adb_close(int fd);
+
+// Helper to automatically close an FD when it goes out of scope.
+class ScopedFd {
+ public:
+ ScopedFd() {
+ }
+
+ ~ScopedFd() {
+ Reset();
+ }
+
+ void Reset(int fd = -1) {
+ if (fd != fd_) {
+ if (valid()) {
+ adb_close(fd_);
+ }
+ fd_ = fd;
+ }
+ }
+
+ int Release() {
+ int temp = fd_;
+ fd_ = -1;
+ return temp;
+ }
+
+ bool valid() const {
+ return fd_ >= 0;
+ }
+
+ int fd() const {
+ return fd_;
+ }
+
+ private:
+ int fd_ = -1;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedFd);
+};
+
#endif
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 37d1146..a856672 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -1029,8 +1029,8 @@
// TODO: when we have libc++ for Windows, use a regular expression instead.
// wait-for-((any|local|usb)-)?(bootloader|device|recovery|sideload)
- char type[20];
- char state[20];
+ char type[20 + 1]; // sscanf's %20[...] doesn't include the NUL.
+ char state[20 + 1];
int length = 0;
if (sscanf(service, "wait-for-%20[a-z]-%20[a-z]%n", type, state, &length) < 2 ||
length != static_cast<int>(strlen(service))) {
@@ -1072,6 +1072,51 @@
return adb_command(cmd);
}
+static bool adb_root(const char* command) {
+ std::string error;
+ ScopedFd fd;
+
+ fd.Reset(adb_connect(android::base::StringPrintf("%s:", command), &error));
+ if (!fd.valid()) {
+ fprintf(stderr, "adb: unable to connect for %s: %s\n", command, error.c_str());
+ return false;
+ }
+
+ // Figure out whether we actually did anything.
+ char buf[256];
+ char* cur = buf;
+ ssize_t bytes_left = sizeof(buf);
+ while (bytes_left > 0) {
+ ssize_t bytes_read = adb_read(fd.fd(), cur, bytes_left);
+ if (bytes_read == 0) {
+ break;
+ } else if (bytes_read < 0) {
+ fprintf(stderr, "adb: error while reading for %s: %s\n", command, strerror(errno));
+ return false;
+ }
+ cur += bytes_read;
+ bytes_left -= bytes_read;
+ }
+
+ if (bytes_left == 0) {
+ fprintf(stderr, "adb: unexpected output length for %s\n", command);
+ return false;
+ }
+
+ fflush(stdout);
+ WriteFdExactly(STDOUT_FILENO, buf, sizeof(buf) - bytes_left);
+ if (cur != buf && strstr(buf, "restarting") == nullptr) {
+ return true;
+ }
+
+ // Give adbd 500ms to kill itself, then wait-for-device for it to come back up.
+ adb_sleep_ms(500);
+ TransportType type;
+ const char* serial;
+ adb_get_transport(&type, &serial);
+ return wait_for_device("wait-for-device", type, serial);
+}
+
// Connects to the device "shell" service with |command| and prints the
// resulting output.
static int send_shell_command(TransportType transport_type, const char* serial,
@@ -1220,6 +1265,9 @@
printf("Now unlock your device and confirm the restore operation.\n");
copy_to_file(tarFd, fd);
+ // Wait until the other side finishes, or it'll get sent SIGHUP.
+ copy_to_file(fd, STDOUT_FILENO);
+
adb_close(fd);
adb_close(tarFd);
return 0;
@@ -1632,8 +1680,6 @@
!strcmp(argv[0], "reboot") ||
!strcmp(argv[0], "reboot-bootloader") ||
!strcmp(argv[0], "usb") ||
- !strcmp(argv[0], "root") ||
- !strcmp(argv[0], "unroot") ||
!strcmp(argv[0], "disable-verity") ||
!strcmp(argv[0], "enable-verity")) {
std::string command;
@@ -1645,8 +1691,9 @@
command = android::base::StringPrintf("%s:", argv[0]);
}
return adb_connect_command(command);
- }
- else if (!strcmp(argv[0], "bugreport")) {
+ } else if (!strcmp(argv[0], "root") || !strcmp(argv[0], "unroot")) {
+ return adb_root(argv[0]) ? 0 : 1;
+ } else if (!strcmp(argv[0], "bugreport")) {
if (argc != 1) return usage();
// No need for shell protocol with bugreport, always disable for
// simplicity.
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index cdbff11..6a9e163 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -60,6 +60,18 @@
}
}
+static bool should_pull_file(mode_t mode) {
+ return mode & (S_IFREG | S_IFBLK | S_IFCHR);
+}
+
+static bool should_push_file(mode_t mode) {
+ mode_t mask = S_IFREG;
+#if !defined(_WIN32)
+ mask |= S_IFLNK;
+#endif
+ return mode & mask;
+}
+
struct copyinfo {
std::string lpath;
std::string rpath;
@@ -483,11 +495,6 @@
#endif
}
- if (!S_ISREG(mode)) {
- sc.Error("local file '%s' has unsupported mode: 0o%o", lpath, mode);
- return false;
- }
-
struct stat st;
if (stat(lpath, &st) == -1) {
sc.Error("failed to stat local file '%s': %s", lpath, strerror(errno));
@@ -619,13 +626,12 @@
if (S_ISDIR(st.st_mode)) {
dirlist.push_back(ci);
} else {
- if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
- sc.Error("skipping special file '%s'", lpath.c_str());
+ if (!should_push_file(st.st_mode)) {
+ sc.Warning("skipping special file '%s' (mode = 0o%o)", lpath.c_str(), st.st_mode);
ci.skip = true;
- } else {
- ci.time = st.st_mtime;
- ci.size = st.st_size;
}
+ ci.time = st.st_mtime;
+ ci.size = st.st_size;
file_list->push_back(ci);
}
}
@@ -767,6 +773,9 @@
success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(),
false, false);
continue;
+ } else if (!should_push_file(st.st_mode)) {
+ sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, st.st_mode);
+ continue;
}
std::string path_holder;
@@ -804,7 +813,7 @@
std::vector<copyinfo> linklist;
// Add an entry for the current directory to ensure it gets created before pulling its contents.
- copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(rpath), S_IFDIR);
+ copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(lpath), S_IFDIR);
file_list->push_back(ci);
// Put the files/dirs in rpath on the lists.
@@ -819,6 +828,10 @@
} else if (S_ISLNK(mode)) {
linklist.push_back(ci);
} else {
+ if (!should_pull_file(ci.mode)) {
+ sc.Warning("skipping special file '%s' (mode = 0o%o)", ci.rpath.c_str(), ci.mode);
+ ci.skip = true;
+ }
ci.time = time;
ci.size = size;
file_list->push_back(ci);
@@ -975,11 +988,6 @@
src_isdir = remote_symlink_isdir(sc, src_path);
}
- if ((src_mode & (S_IFREG | S_IFDIR | S_IFBLK | S_IFCHR)) == 0) {
- sc.Error("skipping remote object '%s' (mode = 0o%o)", src_path, src_mode);
- continue;
- }
-
if (src_isdir) {
std::string dst_dir = dst;
@@ -998,27 +1006,30 @@
success &= copy_remote_dir_local(sc, src_path, dst_dir.c_str(), copy_attrs);
continue;
- } else {
- std::string path_holder;
- if (dst_isdir) {
- // If we're copying a remote file to a local directory, we
- // really want to copy to local_dir + OS_PATH_SEPARATOR +
- // basename(remote).
- path_holder = android::base::StringPrintf("%s%c%s", dst_path, OS_PATH_SEPARATOR,
- adb_basename(src_path).c_str());
- dst_path = path_holder.c_str();
- }
+ } else if (!should_pull_file(src_mode)) {
+ sc.Warning("skipping special file '%s' (mode = 0o%o)", src_path, src_mode);
+ continue;
+ }
- sc.SetExpectedTotalBytes(src_size);
- if (!sync_recv(sc, src_path, dst_path)) {
- success = false;
- continue;
- }
+ std::string path_holder;
+ if (dst_isdir) {
+ // If we're copying a remote file to a local directory, we
+ // really want to copy to local_dir + OS_PATH_SEPARATOR +
+ // basename(remote).
+ path_holder = android::base::StringPrintf("%s%c%s", dst_path, OS_PATH_SEPARATOR,
+ adb_basename(src_path).c_str());
+ dst_path = path_holder.c_str();
+ }
- if (copy_attrs && set_time_and_mode(dst_path, src_time, src_mode) != 0) {
- success = false;
- continue;
- }
+ sc.SetExpectedTotalBytes(src_size);
+ if (!sync_recv(sc, src_path, dst_path)) {
+ success = false;
+ continue;
+ }
+
+ if (copy_attrs && set_time_and_mode(dst_path, src_time, src_mode) != 0) {
+ success = false;
+ continue;
}
}
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index f84447f..ce10708 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -135,37 +135,6 @@
return received;
}
-// Helper to automatically close an FD when it goes out of scope.
-class ScopedFd {
- public:
- ScopedFd() {}
- ~ScopedFd() { Reset(); }
-
- void Reset(int fd=-1) {
- if (fd != fd_) {
- if (valid()) {
- adb_close(fd_);
- }
- fd_ = fd;
- }
- }
-
- int Release() {
- int temp = fd_;
- fd_ = -1;
- return temp;
- }
-
- bool valid() const { return fd_ >= 0; }
-
- int fd() const { return fd_; }
-
- private:
- int fd_ = -1;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedFd);
-};
-
// Creates a socketpair and saves the endpoints to |fd1| and |fd2|.
bool CreateSocketpair(ScopedFd* fd1, ScopedFd* fd2) {
int sockets[2];
@@ -315,7 +284,9 @@
if (type_ == SubprocessType::kPty) {
int fd;
pid_ = forkpty(&fd, pts_name, nullptr, nullptr);
- stdinout_sfd_.Reset(fd);
+ if (pid_ > 0) {
+ stdinout_sfd_.Reset(fd);
+ }
} else {
if (!CreateSocketpair(&stdinout_sfd_, &child_stdinout_sfd)) {
*error = android::base::StringPrintf("failed to create socketpair for stdin/out: %s",
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index ce0f289..81d201e 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -576,8 +576,7 @@
// Closes a file descriptor that came from adb_open() or adb_open_mode(), but
// not designed to take a file descriptor from unix_open(). See the comments
// for adb_open() for more info.
-static __inline__ int adb_close(int fd)
-{
+__inline__ int adb_close(int fd) {
return close(fd);
}
#undef close
diff --git a/adb/test_device.py b/adb/test_device.py
index 2acc444..9dab3ae 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -216,8 +216,8 @@
"""Send data through adb forward and read it back via adb reverse"""
forward_port = 12345
reverse_port = forward_port + 1
- forward_spec = "tcp:" + str(forward_port)
- reverse_spec = "tcp:" + str(reverse_port)
+ forward_spec = 'tcp:' + str(forward_port)
+ reverse_spec = 'tcp:' + str(reverse_port)
forward_setup = False
reverse_setup = False
@@ -739,7 +739,7 @@
# Create some random files and a subdirectory containing more files.
temp_files = make_random_host_files(in_dir=host_dir, num_files=4)
- subdir = os.path.join(host_dir, "subdir")
+ subdir = os.path.join(host_dir, 'subdir')
os.mkdir(subdir)
subdir_temp_files = make_random_host_files(in_dir=subdir,
num_files=4)
@@ -756,7 +756,7 @@
for subdir_temp_file in subdir_temp_files:
remote_path = posixpath.join(self.DEVICE_TEMP_DIR,
# BROKEN: http://b/25394682
- # "subdir",
+ # 'subdir';
temp_file.base_name)
self._verify_remote(temp_file.checksum, remote_path)
@@ -777,11 +777,11 @@
tmp_file.flush()
try:
self.device.push(local=tmp_file.name, remote='/system/')
- self.fail("push should not have succeeded")
+ self.fail('push should not have succeeded')
except subprocess.CalledProcessError as e:
output = e.output
- self.assertIn("Permission denied", output)
+ self.assertIn('Permission denied', output)
def _test_pull(self, remote_file, checksum):
tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
@@ -850,13 +850,13 @@
Bug: http://b/27362811
"""
- if os.name != "posix":
+ if os.name != 'posix':
raise unittest.SkipTest('requires POSIX')
try:
host_dir = tempfile.mkdtemp()
- real_dir = os.path.join(host_dir, "dir")
- symlink = os.path.join(host_dir, "symlink")
+ real_dir = os.path.join(host_dir, 'dir')
+ symlink = os.path.join(host_dir, 'symlink')
os.mkdir(real_dir)
os.symlink(real_dir, symlink)
@@ -882,12 +882,12 @@
def test_pull_dir_symlink_collision(self):
"""Pull a directory into a colliding symlink to directory."""
- if os.name != "posix":
+ if os.name != 'posix':
raise unittest.SkipTest('requires POSIX')
try:
host_dir = tempfile.mkdtemp()
- real_dir = os.path.join(host_dir, "real")
+ real_dir = os.path.join(host_dir, 'real')
tmp_dirname = os.path.basename(self.DEVICE_TEMP_DIR)
symlink = os.path.join(host_dir, tmp_dirname)
os.mkdir(real_dir)
@@ -911,6 +911,30 @@
if host_dir is not None:
shutil.rmtree(host_dir)
+ def test_pull_dir_nonexistent(self):
+ """Pull a directory of files from the device to a nonexistent path."""
+ try:
+ host_dir = tempfile.mkdtemp()
+ dest_dir = os.path.join(host_dir, 'dest')
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ self.device.shell(['mkdir', '-p', self.DEVICE_TEMP_DIR])
+
+ # Populate device directory with random files.
+ temp_files = make_random_device_files(
+ self.device, in_dir=self.DEVICE_TEMP_DIR, num_files=32)
+
+ self.device.pull(remote=self.DEVICE_TEMP_DIR, local=dest_dir)
+
+ for temp_file in temp_files:
+ host_path = os.path.join(dest_dir, temp_file.base_name)
+ self._verify_local(temp_file.checksum, host_path)
+
+ self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
+ finally:
+ if host_dir is not None:
+ shutil.rmtree(host_dir)
+
def test_pull_symlink_dir(self):
"""Pull a symlink to a directory of symlinks to files."""
try:
@@ -966,7 +990,7 @@
try:
host_dir = tempfile.mkdtemp()
- subdir = posixpath.join(self.DEVICE_TEMP_DIR, "subdir")
+ subdir = posixpath.join(self.DEVICE_TEMP_DIR, 'subdir')
self.device.shell(['rm', '-rf', self.DEVICE_TEMP_DIR])
self.device.shell(['mkdir', '-p', subdir])
@@ -987,7 +1011,7 @@
for subdir_temp_file in subdir_temp_files:
local_path = os.path.join(host_dir,
- "subdir",
+ 'subdir',
subdir_temp_file.base_name)
self._verify_local(subdir_temp_file.checksum, local_path)
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 372bedf..f6c9df4 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -260,7 +260,8 @@
} else {
/* Host is connected. Register the transport, and start the
* exchange. */
- register_socket_transport(fd, "host", port, 1);
+ std::string serial = android::base::StringPrintf("host-%d", fd);
+ register_socket_transport(fd, serial.c_str(), port, 1);
if (!WriteFdExactly(fd, _start_req, strlen(_start_req))) {
adb_close(fd);
}
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index cd526d0..e5babed 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -150,6 +150,14 @@
#define UNIMPLEMENTED(level) \
LOG(level) << __PRETTY_FUNCTION__ << " unimplemented "
+#ifdef __clang_analyzer__
+// ClangL static analyzer does not see the conditional statement inside
+// LogMessage's destructor that will abort on FATAL severity.
+#define ABORT_AFTER_LOG_FATAL for (;;abort())
+#else
+#define ABORT_AFTER_LOG_FATAL
+#endif
+
// Check whether condition x holds and LOG(FATAL) if not. The value of the
// expression x is only evaluated once. Extra logging can be appended using <<
// after. For example:
@@ -160,6 +168,7 @@
if (LIKELY((x))) \
; \
else \
+ ABORT_AFTER_LOG_FATAL \
::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
::android::base::FATAL, -1).stream() \
<< "Check failed: " #x << " "
@@ -169,6 +178,7 @@
for (auto _values = ::android::base::MakeEagerEvaluator(LHS, RHS); \
UNLIKELY(!(_values.lhs OP _values.rhs)); \
/* empty */) \
+ ABORT_AFTER_LOG_FATAL \
::android::base::LogMessage(__FILE__, __LINE__, ::android::base::DEFAULT, \
::android::base::FATAL, -1).stream() \
<< "Check failed: " << #LHS << " " << #OP << " " << #RHS \
@@ -192,6 +202,7 @@
if (LIKELY((strcmp(s1, s2) == 0) == sense)) \
; \
else \
+ ABORT_AFTER_LOG_FATAL \
LOG(FATAL) << "Check failed: " \
<< "\"" << s1 << "\"" \
<< (sense ? " == " : " != ") << "\"" << s2 << "\""
@@ -206,6 +217,7 @@
int rc = call args; \
if (rc != 0) { \
errno = rc; \
+ ABORT_AFTER_LOG_FATAL \
PLOG(FATAL) << #call << " failed for " << what; \
} \
} while (false)
diff --git a/bootstat/Android.mk b/bootstat/Android.mk
index c6349c1..3d02752 100644
--- a/bootstat/Android.mk
+++ b/bootstat/Android.mk
@@ -20,32 +20,33 @@
bootstat_lib_src_files := \
boot_event_record_store.cpp \
- event_log_list_builder.cpp
+ event_log_list_builder.cpp \
+ uptime_parser.cpp \
bootstat_src_files := \
- bootstat.cpp
+ bootstat.cpp \
bootstat_test_src_files := \
boot_event_record_store_test.cpp \
event_log_list_builder_test.cpp \
- testrunner.cpp
+ testrunner.cpp \
bootstat_shared_libs := \
libbase \
libcutils \
- liblog
+ liblog \
bootstat_cflags := \
-Wall \
-Wextra \
- -Werror
+ -Werror \
bootstat_cppflags := \
- -Wno-non-virtual-dtor
+ -Wno-non-virtual-dtor \
bootstat_debug_cflags := \
$(bootstat_cflags) \
- -UNDEBUG
+ -UNDEBUG \
# 524291 corresponds to sysui_histogram, from
# frameworks/base/core/java/com/android/internal/logging/EventLogTags.logtags
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index 8282b40..40254f8 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -24,6 +24,7 @@
#include <utility>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include "uptime_parser.h"
namespace {
@@ -51,14 +52,7 @@
}
void BootEventRecordStore::AddBootEvent(const std::string& event) {
- std::string uptime_str;
- if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
- LOG(ERROR) << "Failed to read /proc/uptime";
- }
-
- // Cast intentionally rounds down.
- int32_t uptime = static_cast<int32_t>(strtod(uptime_str.c_str(), NULL));
- AddBootEventWithValue(event, uptime);
+ AddBootEventWithValue(event, bootstat::ParseUptime());
}
// The implementation of AddBootEventValue makes use of the mtime file
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index 0d7bbb0..343f9d0 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -25,6 +25,7 @@
#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
+#include "uptime_parser.h"
using testing::UnorderedElementsAreArray;
@@ -38,17 +39,6 @@
return (abs(a - b) <= FUZZ_SECONDS);
}
-// Returns the uptime as read from /proc/uptime, rounded down to an integer.
-int32_t ReadUptime() {
- std::string uptime_str;
- if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
- return -1;
- }
-
- // Cast to int to round down.
- return static_cast<int32_t>(strtod(uptime_str.c_str(), NULL));
-}
-
// Recursively deletes the directory at |path|.
void DeleteDirectory(const std::string& path) {
typedef std::unique_ptr<DIR, decltype(&closedir)> ScopedDIR;
@@ -110,7 +100,7 @@
BootEventRecordStore store;
store.SetStorePath(GetStorePathForTesting());
- int32_t uptime = ReadUptime();
+ time_t uptime = bootstat::ParseUptime();
ASSERT_NE(-1, uptime);
store.AddBootEvent("cenozoic");
@@ -125,7 +115,7 @@
BootEventRecordStore store;
store.SetStorePath(GetStorePathForTesting());
- int32_t uptime = ReadUptime();
+ time_t uptime = bootstat::ParseUptime();
ASSERT_NE(-1, uptime);
store.AddBootEvent("cretaceous");
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index c199190..9214e6d 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -20,6 +20,7 @@
#include <getopt.h>
#include <unistd.h>
+#include <cmath>
#include <cstddef>
#include <cstdio>
#include <ctime>
@@ -31,6 +32,7 @@
#include <log/log.h>
#include "boot_event_record_store.h"
#include "event_log_list_builder.h"
+#include "uptime_parser.h"
namespace {
@@ -134,6 +136,30 @@
{"Reboot", 18},
{"rtc", 19},
{"edl", 20},
+ {"oem_pon1", 21},
+ {"oem_powerkey", 22},
+ {"oem_unknown_reset", 23},
+ {"srto: HWWDT reset SC", 24},
+ {"srto: HWWDT reset platform", 25},
+ {"srto: bootloader", 26},
+ {"srto: kernel panic", 27},
+ {"srto: kernel watchdog reset", 28},
+ {"srto: normal", 29},
+ {"srto: reboot", 30},
+ {"srto: reboot-bootloader", 31},
+ {"srto: security watchdog reset", 32},
+ {"srto: wakesrc", 33},
+ {"srto: watchdog", 34},
+ {"srto:1-1", 35},
+ {"srto:omap_hsmm", 36},
+ {"srto:phy0", 37},
+ {"srto:rtc0", 38},
+ {"srto:touchpad", 39},
+ {"watchdog", 40},
+ {"watchdogr", 41},
+ {"wdog_bark", 42},
+ {"wdog_bite", 43},
+ {"wdog_reset", 44},
};
// Converts a string value representing the reason the system booted to an
@@ -149,6 +175,37 @@
return kUnknownBootReason;
}
+// Records several metrics related to the time it takes to boot the device,
+// including disambiguating boot time on encrypted or non-encrypted devices.
+void RecordBootComplete() {
+ BootEventRecordStore boot_event_store;
+ time_t uptime = bootstat::ParseUptime();
+
+ BootEventRecordStore::BootEventRecord record;
+
+ // post_decrypt_time_elapsed is only logged on encrypted devices.
+ if (boot_event_store.GetBootEvent("post_decrypt_time_elapsed", &record)) {
+ // Log the amount of time elapsed until the device is decrypted, which
+ // includes the variable amount of time the user takes to enter the
+ // decryption password.
+ boot_event_store.AddBootEventWithValue("boot_decryption_complete", uptime);
+
+ // Subtract the decryption time to normalize the boot cycle timing.
+ time_t boot_complete = uptime - record.second;
+ boot_event_store.AddBootEventWithValue("boot_complete_post_decrypt",
+ boot_complete);
+
+
+ } else {
+ boot_event_store.AddBootEventWithValue("boot_complete_no_encryption",
+ uptime);
+ }
+
+ // Record the total time from device startup to boot complete, regardless of
+ // encryption state.
+ boot_event_store.AddBootEventWithValue("boot_complete", uptime);
+}
+
// Records the boot_reason metric by querying the ro.boot.bootreason system
// property.
void RecordBootReason() {
@@ -166,17 +223,29 @@
time_t current_time_utc = time(nullptr);
+ static const char* factory_reset_current_time = "factory_reset_current_time";
+ if (current_time_utc < 0) {
+ // UMA does not display negative values in buckets, so convert to positive.
+ LogBootEvent(factory_reset_current_time, std::abs(current_time_utc));
+ return;
+ } else {
+ LogBootEvent(factory_reset_current_time, current_time_utc);
+ }
+
// The factory_reset boot event does not exist after the device is reset, so
// use this signal to mark the time of the factory reset.
if (!boot_event_store.GetBootEvent("factory_reset", &record)) {
boot_event_store.AddBootEventWithValue("factory_reset", current_time_utc);
- boot_event_store.AddBootEventWithValue("time_since_factory_reset", 0);
+
+ // Don't log the time_since_factory_reset until some time has elapsed.
+ // The data is not meaningful yet and skews the histogram buckets.
return;
}
// Calculate and record the difference in time between now and the
// factory_reset time.
time_t factory_reset_utc = record.second;
+ LogBootEvent("factory_reset_record_value", factory_reset_utc);
time_t time_since_factory_reset = difftime(current_time_utc,
factory_reset_utc);
boot_event_store.AddBootEventWithValue("time_since_factory_reset",
@@ -192,6 +261,7 @@
LOG(INFO) << "Service started: " << cmd_line;
int option_index = 0;
+ static const char boot_complete_str[] = "record_boot_complete";
static const char boot_reason_str[] = "record_boot_reason";
static const char factory_reset_str[] = "record_time_since_factory_reset";
static const struct option long_options[] = {
@@ -199,6 +269,7 @@
{ "log", no_argument, NULL, 'l' },
{ "print", no_argument, NULL, 'p' },
{ "record", required_argument, NULL, 'r' },
+ { boot_complete_str, no_argument, NULL, 0 },
{ boot_reason_str, no_argument, NULL, 0 },
{ factory_reset_str, no_argument, NULL, 0 },
{ NULL, 0, NULL, 0 }
@@ -210,7 +281,9 @@
// This case handles long options which have no single-character mapping.
case 0: {
const std::string option_name = long_options[option_index].name;
- if (option_name == boot_reason_str) {
+ if (option_name == boot_complete_str) {
+ RecordBootComplete();
+ } else if (option_name == boot_reason_str) {
RecordBootReason();
} else if (option_name == factory_reset_str) {
RecordFactoryReset();
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index 13ef27e..ba8f81c 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -3,12 +3,23 @@
on post-fs-data
mkdir /data/misc/bootstat 0700 root root
-# This marker, boot animation stopped, is considered the point at which the
+# Record the time at which the user has successfully entered the pin to decrypt
+# the device, /data is decrypted, and the system is entering the main boot phase.
+#
+# post-fs-data: /data is writable
+# property:init.svc.bootanim=running: The boot animation is running
+on post-fs-data && property:init.svc.bootanim=running
+ exec - root root -- /system/bin/bootstat -r post_decrypt_time_elapsed
+
+# The first marker, boot animation stopped, is considered the point at which
# the user may interact with the device, so it is a good proxy for the boot
# complete signal.
-on property:init.svc.bootanim=stopped
- # Record boot_complete timing event.
- exec - root root -- /system/bin/bootstat -r boot_complete
+#
+# The second marker ensures an encrypted device is decrypted before logging
+# boot time data.
+on property:init.svc.bootanim=stopped && property:vold.decrypt=trigger_restart_framework
+ # Record boot_complete and related stats (decryption, etc).
+ exec - root root -- /system/bin/bootstat --record_boot_complete
# Record the boot reason.
exec - root root -- /system/bin/bootstat --record_boot_reason
diff --git a/bootstat/uptime_parser.cpp b/bootstat/uptime_parser.cpp
new file mode 100644
index 0000000..7c2034c
--- /dev/null
+++ b/bootstat/uptime_parser.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 "uptime_parser.h"
+
+#include <time.h>
+#include <cstdlib>
+#include <string>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+
+namespace bootstat {
+
+time_t ParseUptime() {
+ std::string uptime_str;
+ if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
+ PLOG(ERROR) << "Failed to read /proc/uptime";
+ return -1;
+ }
+
+ // Cast intentionally rounds down.
+ return static_cast<time_t>(strtod(uptime_str.c_str(), NULL));
+}
+
+} // namespace bootstat
\ No newline at end of file
diff --git a/bootstat/uptime_parser.h b/bootstat/uptime_parser.h
new file mode 100644
index 0000000..756ae9b
--- /dev/null
+++ b/bootstat/uptime_parser.h
@@ -0,0 +1,29 @@
+/*
+ * 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 UPTIME_PARSER_H_
+#define UPTIME_PARSER_H_
+
+#include <time.h>
+
+namespace bootstat {
+
+// Returns the number of seconds the system has been on since reboot.
+time_t ParseUptime();
+
+} // namespace bootstat
+
+#endif // UPTIME_PARSER_H_
\ No newline at end of file
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index 9e4f1f7..6469db4 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -15,6 +15,7 @@
debuggerd.cpp \
elf_utils.cpp \
getevent.cpp \
+ signal_sender.cpp \
tombstone.cpp \
utility.cpp \
diff --git a/debuggerd/backtrace.cpp b/debuggerd/backtrace.cpp
index b6916e5..32843d8 100644
--- a/debuggerd/backtrace.cpp
+++ b/debuggerd/backtrace.cpp
@@ -91,7 +91,8 @@
if (backtrace->Unwind(0)) {
dump_backtrace_to_log(backtrace.get(), log, " ");
} else {
- ALOGE("Unwind failed: tid = %d", tid);
+ ALOGE("Unwind failed: tid = %d: %s", tid,
+ backtrace->GetErrorString(backtrace->GetError()).c_str());
}
}
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index ac1c58c..97f4096 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -15,21 +15,20 @@
*/
#include <dirent.h>
+#include <elf.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
-#include <sys/types.h>
-#include <time.h>
-
-#include <elf.h>
#include <sys/poll.h>
#include <sys/prctl.h>
#include <sys/ptrace.h>
#include <sys/stat.h>
+#include <sys/types.h>
#include <sys/wait.h>
+#include <time.h>
#include <set>
@@ -48,6 +47,7 @@
#include "backtrace.h"
#include "getevent.h"
+#include "signal_sender.h"
#include "tombstone.h"
#include "utility.h"
@@ -248,8 +248,8 @@
return 0;
}
-static bool should_attach_gdb(debugger_request_t* request) {
- if (request->action == DEBUGGER_ACTION_CRASH) {
+static bool should_attach_gdb(const debugger_request_t& request) {
+ if (request.action == DEBUGGER_ACTION_CRASH) {
return property_get_bool("debug.debuggerd.wait_for_gdb", false);
}
return false;
@@ -374,7 +374,8 @@
}
static bool perform_dump(const debugger_request_t& request, int fd, int tombstone_fd,
- BacktraceMap* backtrace_map, const std::set<pid_t>& siblings) {
+ BacktraceMap* backtrace_map, const std::set<pid_t>& siblings,
+ int* crash_signal) {
if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) {
ALOGE("debuggerd: failed to respond to client: %s\n", strerror(errno));
return false;
@@ -416,11 +417,7 @@
#endif
case SIGTRAP:
ALOGV("stopped -- fatal signal\n");
- // Send a SIGSTOP to the process to make all of
- // the non-signaled threads stop moving. Without
- // this we get a lot of "ptrace detach failed:
- // No such process".
- kill(request.pid, SIGSTOP);
+ *crash_signal = signal;
engrave_tombstone(tombstone_fd, backtrace_map, request.pid, request.tid, siblings, signal,
request.original_si_code, request.abort_msg_address);
break;
@@ -449,104 +446,7 @@
return true;
}
-static bool fork_signal_sender(int* in_fd, int* out_fd, pid_t* sender_pid, pid_t target_pid) {
- int input_pipe[2];
- int output_pipe[2];
- if (pipe(input_pipe) != 0) {
- ALOGE("debuggerd: failed to create input pipe for signal sender: %s", strerror(errno));
- return false;
- }
-
- if (pipe(output_pipe) != 0) {
- close(input_pipe[0]);
- close(input_pipe[1]);
- ALOGE("debuggerd: failed to create output pipe for signal sender: %s", strerror(errno));
- return false;
- }
-
- pid_t fork_pid = fork();
- if (fork_pid == -1) {
- ALOGE("debuggerd: failed to initialize signal sender: fork failed: %s", strerror(errno));
- return false;
- } else if (fork_pid == 0) {
- close(input_pipe[1]);
- close(output_pipe[0]);
- auto wait = [=]() {
- char buf[1];
- if (TEMP_FAILURE_RETRY(read(input_pipe[0], buf, 1)) != 1) {
- ALOGE("debuggerd: signal sender failed to read from pipe");
- exit(1);
- }
- };
- auto notify_done = [=]() {
- if (TEMP_FAILURE_RETRY(write(output_pipe[1], "", 1)) != 1) {
- ALOGE("debuggerd: signal sender failed to write to pipe");
- exit(1);
- }
- };
-
- wait();
- if (kill(target_pid, SIGSTOP) != 0) {
- ALOGE("debuggerd: failed to stop target '%d': %s", target_pid, strerror(errno));
- }
- notify_done();
-
- wait();
- if (kill(target_pid, SIGCONT) != 0) {
- ALOGE("debuggerd: failed to resume target '%d': %s", target_pid, strerror(errno));
- }
- notify_done();
-
- exit(0);
- } else {
- close(input_pipe[0]);
- close(output_pipe[1]);
- *in_fd = input_pipe[1];
- *out_fd = output_pipe[0];
- *sender_pid = fork_pid;
- return true;
- }
-}
-
-static void handle_request(int fd) {
- ALOGV("handle_request(%d)\n", fd);
-
- ScopedFd closer(fd);
- debugger_request_t request;
- memset(&request, 0, sizeof(request));
- int status = read_request(fd, &request);
- if (status != 0) {
- return;
- }
-
- ALOGV("BOOM: pid=%d uid=%d gid=%d tid=%d\n", request.pid, request.uid, request.gid, request.tid);
-
-#if defined(__LP64__)
- // On 64 bit systems, requests to dump 32 bit and 64 bit tids come
- // to the 64 bit debuggerd. If the process is a 32 bit executable,
- // redirect the request to the 32 bit debuggerd.
- if (is32bit(request.tid)) {
- // Only dump backtrace and dump tombstone requests can be redirected.
- if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE ||
- request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
- redirect_to_32(fd, &request);
- } else {
- ALOGE("debuggerd: Not allowed to redirect action %d to 32 bit debuggerd\n", request.action);
- }
- return;
- }
-#endif
-
- // Fork a child to handle the rest of the request.
- pid_t fork_pid = fork();
- if (fork_pid == -1) {
- ALOGE("debuggerd: failed to fork: %s\n", strerror(errno));
- return;
- } else if (fork_pid != 0) {
- waitpid(fork_pid, nullptr, 0);
- return;
- }
-
+static void worker_process(int fd, debugger_request_t& request) {
// Open the tombstone file if we need it.
std::string tombstone_path;
int tombstone_fd = -1;
@@ -587,10 +487,7 @@
// Don't attach to the sibling threads if we want to attach gdb.
// Supposedly, it makes the process less reliable.
- bool attach_gdb = should_attach_gdb(&request);
- int signal_in_fd = -1;
- int signal_out_fd = -1;
- pid_t signal_pid = 0;
+ bool attach_gdb = should_attach_gdb(request);
if (attach_gdb) {
// Open all of the input devices we need to listen for VOLUMEDOWN before dropping privileges.
if (init_getevent() != 0) {
@@ -598,21 +495,8 @@
attach_gdb = false;
}
- // Fork a process that stays root, and listens on a pipe to pause and resume the target.
- if (!fork_signal_sender(&signal_in_fd, &signal_out_fd, &signal_pid, request.pid)) {
- attach_gdb = false;
- }
}
- auto notify_signal_sender = [=]() {
- char buf[1];
- if (TEMP_FAILURE_RETRY(write(signal_in_fd, "", 1)) != 1) {
- ALOGE("debuggerd: failed to notify signal process: %s", strerror(errno));
- } else if (TEMP_FAILURE_RETRY(read(signal_out_fd, buf, 1)) != 1) {
- ALOGE("debuggerd: failed to read response from signal process: %s", strerror(errno));
- }
- };
-
std::set<pid_t> siblings;
if (!attach_gdb) {
ptrace_siblings(request.pid, request.tid, siblings);
@@ -624,19 +508,26 @@
bool succeeded = false;
// Now that we've done everything that requires privileges, we can drop them.
- if (drop_privileges()) {
- succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), siblings);
- if (succeeded) {
- if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
- if (!tombstone_path.empty()) {
- write(fd, tombstone_path.c_str(), tombstone_path.length());
- }
+ if (!drop_privileges()) {
+ ALOGE("debuggerd: failed to drop privileges, exiting");
+ _exit(1);
+ }
+
+ int crash_signal = SIGKILL;
+ succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), siblings, &crash_signal);
+ if (succeeded) {
+ if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
+ if (!tombstone_path.empty()) {
+ write(fd, tombstone_path.c_str(), tombstone_path.length());
}
}
+ }
- if (attach_gdb) {
- // Tell the signal process to send SIGSTOP to the target.
- notify_signal_sender();
+ if (attach_gdb) {
+ // Tell the signal process to send SIGSTOP to the target.
+ if (!send_signal(request.pid, 0, SIGSTOP)) {
+ ALOGE("debuggerd: failed to stop process for gdb attach: %s", strerror(errno));
+ attach_gdb = false;
}
}
@@ -648,20 +539,137 @@
ptrace(PTRACE_DETACH, sibling, 0, 0);
}
+ // Send the signal back to the process if it crashed and we're not waiting for gdb.
+ if (!attach_gdb && request.action == DEBUGGER_ACTION_CRASH) {
+ if (!send_signal(request.pid, request.tid, crash_signal)) {
+ ALOGE("debuggerd: failed to kill process %d: %s", request.pid, strerror(errno));
+ }
+ }
+
// Wait for gdb, if requested.
if (attach_gdb && succeeded) {
wait_for_user_action(request);
// Tell the signal process to send SIGCONT to the target.
- notify_signal_sender();
+ if (!send_signal(request.pid, 0, SIGCONT)) {
+ ALOGE("debuggerd: failed to resume process %d: %s", request.pid, strerror(errno));
+ }
uninit_getevent();
- waitpid(signal_pid, nullptr, 0);
}
exit(!succeeded);
}
+static void monitor_worker_process(int child_pid, const debugger_request_t& request) {
+ struct timespec timeout = {.tv_sec = 10, .tv_nsec = 0 };
+ if (should_attach_gdb(request)) {
+ // If wait_for_gdb is enabled, set the timeout to something large.
+ timeout.tv_sec = INT_MAX;
+ }
+
+ sigset_t signal_set;
+ sigemptyset(&signal_set);
+ sigaddset(&signal_set, SIGCHLD);
+
+ bool kill_worker = false;
+ bool kill_target = false;
+ bool kill_self = false;
+
+ int status;
+ siginfo_t siginfo;
+ int signal = TEMP_FAILURE_RETRY(sigtimedwait(&signal_set, &siginfo, &timeout));
+ if (signal == SIGCHLD) {
+ pid_t rc = waitpid(0, &status, WNOHANG | WUNTRACED);
+ if (rc != child_pid) {
+ ALOGE("debuggerd: waitpid returned unexpected pid (%d), committing murder-suicide", rc);
+ kill_worker = true;
+ kill_target = true;
+ kill_self = true;
+ }
+
+ if (WIFSIGNALED(status)) {
+ ALOGE("debuggerd: worker process %d terminated due to signal %d", child_pid, WTERMSIG(status));
+ kill_worker = false;
+ kill_target = true;
+ } else if (WIFSTOPPED(status)) {
+ ALOGE("debuggerd: worker process %d stopped due to signal %d", child_pid, WSTOPSIG(status));
+ kill_worker = true;
+ kill_target = true;
+ }
+ } else {
+ ALOGE("debuggerd: worker process %d timed out", child_pid);
+ kill_worker = true;
+ kill_target = true;
+ }
+
+ if (kill_worker) {
+ // Something bad happened, kill the worker.
+ if (kill(child_pid, SIGKILL) != 0) {
+ ALOGE("debuggerd: failed to kill worker process %d: %s", child_pid, strerror(errno));
+ } else {
+ waitpid(child_pid, &status, 0);
+ }
+ }
+
+ if (kill_target) {
+ // Resume or kill the target, depending on what the initial request was.
+ if (request.action == DEBUGGER_ACTION_CRASH) {
+ ALOGE("debuggerd: killing target %d", request.pid);
+ kill(request.pid, SIGKILL);
+ } else {
+ ALOGE("debuggerd: resuming target %d", request.pid);
+ kill(request.pid, SIGCONT);
+ }
+ }
+
+ if (kill_self) {
+ stop_signal_sender();
+ _exit(1);
+ }
+}
+
+static void handle_request(int fd) {
+ ALOGV("handle_request(%d)\n", fd);
+
+ ScopedFd closer(fd);
+ debugger_request_t request;
+ memset(&request, 0, sizeof(request));
+ int status = read_request(fd, &request);
+ if (status != 0) {
+ return;
+ }
+
+ ALOGW("debuggerd: handling request: pid=%d uid=%d gid=%d tid=%d\n", request.pid, request.uid,
+ request.gid, request.tid);
+
+#if defined(__LP64__)
+ // On 64 bit systems, requests to dump 32 bit and 64 bit tids come
+ // to the 64 bit debuggerd. If the process is a 32 bit executable,
+ // redirect the request to the 32 bit debuggerd.
+ if (is32bit(request.tid)) {
+ // Only dump backtrace and dump tombstone requests can be redirected.
+ if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE ||
+ request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) {
+ redirect_to_32(fd, &request);
+ } else {
+ ALOGE("debuggerd: Not allowed to redirect action %d to 32 bit debuggerd\n", request.action);
+ }
+ return;
+ }
+#endif
+
+ // Fork a child to handle the rest of the request.
+ pid_t fork_pid = fork();
+ if (fork_pid == -1) {
+ ALOGE("debuggerd: failed to fork: %s\n", strerror(errno));
+ } else if (fork_pid == 0) {
+ worker_process(fd, request);
+ } else {
+ monitor_worker_process(fork_pid, request);
+ }
+}
+
static int do_server() {
// debuggerd crashes can't be reported to debuggerd.
// Reset all of the crash handlers.
@@ -678,17 +686,22 @@
// Ignore failed writes to closed sockets
signal(SIGPIPE, SIG_IGN);
- struct sigaction act;
- act.sa_handler = SIG_DFL;
- sigemptyset(&act.sa_mask);
- sigaddset(&act.sa_mask,SIGCHLD);
- act.sa_flags = SA_NOCLDWAIT;
- sigaction(SIGCHLD, &act, 0);
+ // Block SIGCHLD so we can sigtimedwait for it.
+ sigset_t sigchld;
+ sigemptyset(&sigchld);
+ sigaddset(&sigchld, SIGCHLD);
+ sigprocmask(SIG_SETMASK, &sigchld, nullptr);
int s = socket_local_server(SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_ABSTRACT,
SOCK_STREAM | SOCK_CLOEXEC);
if (s == -1) return 1;
+ // Fork a process that stays root, and listens on a pipe to pause and resume the target.
+ if (!start_signal_sender()) {
+ ALOGE("debuggerd: failed to fork signal sender");
+ return 1;
+ }
+
ALOGI("debuggerd: starting\n");
for (;;) {
diff --git a/debuggerd/elf_utils.cpp b/debuggerd/elf_utils.cpp
index 3f0dbde..9959f2e 100644
--- a/debuggerd/elf_utils.cpp
+++ b/debuggerd/elf_utils.cpp
@@ -63,10 +63,10 @@
if (nhdr.n_type == NT_GNU_BUILD_ID) {
// Skip the name (which is the owner and should be "GNU").
addr += NOTE_ALIGN(nhdr.n_namesz);
- uint8_t build_id_data[128];
- if (nhdr.n_namesz > sizeof(build_id_data)) {
- ALOGE("Possible corrupted note, name size value is too large: %u",
- nhdr.n_namesz);
+ uint8_t build_id_data[160];
+ if (nhdr.n_descsz > sizeof(build_id_data)) {
+ ALOGE("Possible corrupted note, desc size value is too large: %u",
+ nhdr.n_descsz);
return false;
}
if (backtrace->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) {
diff --git a/debuggerd/signal_sender.cpp b/debuggerd/signal_sender.cpp
new file mode 100644
index 0000000..1cfb704
--- /dev/null
+++ b/debuggerd/signal_sender.cpp
@@ -0,0 +1,152 @@
+/*
+ * 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 <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <log/logger.h>
+
+#include "signal_sender.h"
+
+static int signal_fd = -1;
+static pid_t signal_pid;
+struct signal_message {
+ pid_t pid;
+ pid_t tid;
+ int signal;
+};
+
+// Fork a process to send signals for the worker processes to use after they've dropped privileges.
+bool start_signal_sender() {
+ if (signal_pid != 0) {
+ ALOGE("debuggerd: attempted to start signal sender multiple times");
+ return false;
+ }
+
+ int sfd[2];
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sfd) != 0) {
+ ALOGE("debuggerd: failed to create socketpair for signal sender: %s", strerror(errno));
+ return false;
+ }
+
+ pid_t parent = getpid();
+ pid_t fork_pid = fork();
+ if (fork_pid == -1) {
+ ALOGE("debuggerd: failed to initialize signal sender: fork failed: %s", strerror(errno));
+ return false;
+ } else if (fork_pid == 0) {
+ close(sfd[1]);
+
+ while (true) {
+ signal_message msg;
+ int rc = TEMP_FAILURE_RETRY(read(sfd[0], &msg, sizeof(msg)));
+ if (rc < 0) {
+ ALOGE("debuggerd: signal sender failed to read from socket");
+ break;
+ } else if (rc != sizeof(msg)) {
+ ALOGE("debuggerd: signal sender read unexpected number of bytes: %d", rc);
+ break;
+ }
+
+ // Report success after sending a signal
+ int err = 0;
+ if (msg.tid > 0) {
+ if (syscall(SYS_tgkill, msg.pid, msg.tid, msg.signal) != 0) {
+ err = errno;
+ }
+ } else {
+ if (kill(msg.pid, msg.signal) != 0) {
+ err = errno;
+ }
+ }
+
+ if (TEMP_FAILURE_RETRY(write(sfd[0], &err, sizeof(err))) < 0) {
+ ALOGE("debuggerd: signal sender failed to write: %s", strerror(errno));
+ }
+ }
+
+ // Our parent proably died, but if not, kill them.
+ if (getppid() == parent) {
+ kill(parent, SIGKILL);
+ }
+ _exit(1);
+ } else {
+ close(sfd[0]);
+ signal_fd = sfd[1];
+ signal_pid = fork_pid;
+ return true;
+ }
+}
+
+bool stop_signal_sender() {
+ if (signal_pid <= 0) {
+ return false;
+ }
+
+ if (kill(signal_pid, SIGKILL) != 0) {
+ ALOGE("debuggerd: failed to kill signal sender: %s", strerror(errno));
+ return false;
+ }
+
+ close(signal_fd);
+ signal_fd = -1;
+
+ int status;
+ waitpid(signal_pid, &status, 0);
+ signal_pid = 0;
+
+ return true;
+}
+
+bool send_signal(pid_t pid, pid_t tid, int signal) {
+ if (signal_fd == -1) {
+ ALOGE("debuggerd: attempted to send signal before signal sender was started");
+ errno = EHOSTUNREACH;
+ return false;
+ }
+
+ signal_message msg = {.pid = pid, .tid = tid, .signal = signal };
+ if (TEMP_FAILURE_RETRY(write(signal_fd, &msg, sizeof(msg))) < 0) {
+ ALOGE("debuggerd: failed to send message to signal sender: %s", strerror(errno));
+ errno = EHOSTUNREACH;
+ return false;
+ }
+
+ int response;
+ ssize_t rc = TEMP_FAILURE_RETRY(read(signal_fd, &response, sizeof(response)));
+ if (rc == 0) {
+ ALOGE("debuggerd: received EOF from signal sender");
+ errno = EHOSTUNREACH;
+ return false;
+ } else if (rc < 0) {
+ ALOGE("debuggerd: failed to receive response from signal sender: %s", strerror(errno));
+ errno = EHOSTUNREACH;
+ return false;
+ }
+
+ if (response == 0) {
+ return true;
+ }
+
+ errno = response;
+ return false;
+}
diff --git a/debuggerd/signal_sender.h b/debuggerd/signal_sender.h
new file mode 100644
index 0000000..0443272
--- /dev/null
+++ b/debuggerd/signal_sender.h
@@ -0,0 +1,30 @@
+/*
+ * 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 _DEBUGGERD_SIGNAL_SENDER_H
+#define _DEBUGGERD_SIGNAL_SENDER_H
+
+#include <sys/types.h>
+
+bool start_signal_sender();
+bool stop_signal_sender();
+
+// Sends a signal to a target process or thread.
+// If tid is greater than zero, this performs tgkill(pid, tid, signal).
+// Otherwise, it performs kill(pid, signal).
+bool send_signal(pid_t pid, pid_t tid, int signal);
+
+#endif
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index c47a585..02aff55 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -600,7 +600,7 @@
fstab->recs[top_idx].fs_type);
if (fs_mgr_is_encryptable(&fstab->recs[top_idx]) &&
strcmp(fstab->recs[top_idx].key_loc, KEY_IN_FOOTER)) {
- int fd = open(fstab->recs[top_idx].key_loc, O_WRONLY, 0644);
+ int fd = open(fstab->recs[top_idx].key_loc, O_WRONLY);
if (fd >= 0) {
INFO("%s(): also wipe %s\n", __func__, fstab->recs[top_idx].key_loc);
wipe_block_device(fd, get_file_size(fd));
diff --git a/fs_mgr/fs_mgr_format.c b/fs_mgr/fs_mgr_format.c
index 853bf0b..c63ff67 100644
--- a/fs_mgr/fs_mgr_format.c
+++ b/fs_mgr/fs_mgr_format.c
@@ -36,7 +36,7 @@
uint64_t dev_sz;
int fd, rc = 0;
- if ((fd = open(fs_blkdev, O_WRONLY, 0644)) < 0) {
+ if ((fd = open(fs_blkdev, O_WRONLY)) < 0) {
ERROR("Cannot open block device. %s\n", strerror(errno));
return -1;
}
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index b4fdab0..7254cf2 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -76,7 +76,7 @@
void store_sid(uint32_t uid, uint64_t sid) {
char filename[21];
- sprintf(filename, "%u", uid);
+ snprintf(filename, sizeof(filename), "%u", uid);
int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
if (fd < 0) {
ALOGE("could not open file: %s: %s", filename, strerror(errno));
@@ -102,7 +102,7 @@
void maybe_store_sid(uint32_t uid, uint64_t sid) {
char filename[21];
- sprintf(filename, "%u", uid);
+ snprintf(filename, sizeof(filename), "%u", uid);
if (access(filename, F_OK) == -1) {
store_sid(uid, sid);
}
@@ -111,7 +111,7 @@
uint64_t read_sid(uint32_t uid) {
char filename[21];
uint64_t sid;
- sprintf(filename, "%u", uid);
+ snprintf(filename, sizeof(filename), "%u", uid);
int fd = open(filename, O_RDONLY);
if (fd < 0) return 0;
read(fd, &sid, sizeof(sid));
@@ -121,7 +121,7 @@
void clear_sid(uint32_t uid) {
char filename[21];
- sprintf(filename, "%u", uid);
+ snprintf(filename, sizeof(filename), "%u", uid);
if (remove(filename) < 0) {
ALOGE("%s: could not remove file [%s], attempting 0 write", __func__, strerror(errno));
store_sid(uid, 0);
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index b33dd28..69647de 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -613,19 +613,15 @@
}
}
- // This indicates that there is no charger driver registered.
// Typically the case for devices which do not have a battery and
// and are always plugged into AC mains.
- if (!mChargerNames.size()) {
- KLOG_ERROR(LOG_TAG, "No charger supplies found\n");
- mBatteryFixedCapacity = ALWAYS_PLUGGED_CAPACITY;
- mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
- mAlwaysPluggedDevice = true;
- }
if (!mBatteryDevicePresent) {
KLOG_WARNING(LOG_TAG, "No battery devices found\n");
hc->periodic_chores_interval_fast = -1;
hc->periodic_chores_interval_slow = -1;
+ mBatteryFixedCapacity = ALWAYS_PLUGGED_CAPACITY;
+ mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;
+ mAlwaysPluggedDevice = true;
} else {
if (mHealthdConfig->batteryStatusPath.isEmpty())
KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");
diff --git a/include/backtrace/Backtrace.h b/include/backtrace/Backtrace.h
index f440bd2..c896ab8 100644
--- a/include/backtrace/Backtrace.h
+++ b/include/backtrace/Backtrace.h
@@ -34,6 +34,24 @@
typedef uint32_t word_t;
#endif
+enum BacktraceUnwindError : uint32_t {
+ BACKTRACE_UNWIND_NO_ERROR,
+ // Something failed while trying to perform the setup to begin the unwind.
+ BACKTRACE_UNWIND_ERROR_SETUP_FAILED,
+ // There is no map information to use with the unwind.
+ BACKTRACE_UNWIND_ERROR_MAP_MISSING,
+ // An error occurred that indicates a programming error.
+ BACKTRACE_UNWIND_ERROR_INTERNAL,
+ // The thread to unwind has disappeared before the unwind can begin.
+ BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST,
+ // The thread to unwind has not responded to a signal in a timely manner.
+ BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT,
+ // Attempt to do an unsupported operation.
+ BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION,
+ // Attempt to do an offline unwind without a context.
+ BACKTRACE_UNWIND_ERROR_NO_CONTEXT,
+};
+
struct backtrace_frame_data_t {
size_t num; // The current fame number.
uintptr_t pc; // The absolute pc.
@@ -127,6 +145,10 @@
BacktraceMap* GetMap() { return map_; }
+ BacktraceUnwindError GetError() { return error_; }
+
+ std::string GetErrorString(BacktraceUnwindError error);
+
protected:
Backtrace(pid_t pid, pid_t tid, BacktraceMap* map);
@@ -145,6 +167,8 @@
bool map_shared_;
std::vector<backtrace_frame_data_t> frames_;
+
+ BacktraceUnwindError error_;
};
#endif // _BACKTRACE_BACKTRACE_H
diff --git a/init/init.cpp b/init/init.cpp
index 9e6143b..704597d 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -74,8 +74,7 @@
static char qemu[32];
-int have_console;
-std::string console_name = "/dev/console";
+std::vector<std::string> console_names;
static time_t process_needs_restart;
const char *ENV[32];
@@ -298,38 +297,23 @@
static int console_init_action(const std::vector<std::string>& args)
{
- std::string console = property_get("ro.boot.console");
- if (!console.empty()) {
- console_name = "/dev/" + console;
+ std::vector<std::string> consoles;
+ std::string c_prop = property_get("ro.boot.console");
+ if (c_prop.empty()) {
+ // Property is missing, so check the system console by default.
+ consoles.emplace_back(DEFAULT_CONSOLE);
+ } else {
+ consoles = android::base::Split(c_prop, ":");
}
- int fd = open(console_name.c_str(), O_RDWR | O_CLOEXEC);
- if (fd >= 0)
- have_console = 1;
- close(fd);
-
- fd = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
- if (fd >= 0) {
- const char *msg;
- msg = "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- "\n" // console is 40 cols x 30 lines
- "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- "\n"
- " A N D R O I D ";
- write(fd, msg, strlen(msg));
- close(fd);
+ for (const auto& c : consoles) {
+ std::string console = "/dev/" + c;
+ int fd = open(console.c_str(), O_RDWR | O_CLOEXEC);
+ if (fd != -1) {
+ console_names.emplace_back(c);
+ close(fd);
+ }
}
-
return 0;
}
@@ -460,24 +444,6 @@
return true;
}
-int selinux_reload_policy(void)
-{
- INFO("SELinux: Attempting to reload policy files\n");
-
- if (selinux_android_reload_policy() == -1) {
- return -1;
- }
-
- if (sehandle)
- selabel_close(sehandle);
-
- if (sehandle_prop)
- selabel_close(sehandle_prop);
-
- selinux_init_all_handles();
- return 0;
-}
-
static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
property_audit_data *d = reinterpret_cast<property_audit_data*>(data);
diff --git a/init/init.h b/init/init.h
index 345d442..c844489 100644
--- a/init/init.h
+++ b/init/init.h
@@ -18,16 +18,17 @@
#define _INIT_INIT_H
#include <string>
+#include <vector>
class Action;
class Service;
#define COMMAND_RETRY_TIMEOUT 5
+#define DEFAULT_CONSOLE "console"
extern const char *ENV[32];
extern bool waiting_for_exec;
-extern int have_console;
-extern std::string console_name;
+extern std::vector<std::string> console_names;
extern struct selabel_handle *sehandle;
extern struct selabel_handle *sehandle_prop;
@@ -35,8 +36,6 @@
void property_changed(const char *name, const char *value);
-int selinux_reload_policy(void);
-
void register_epoll_handler(int fd, void (*fn)());
int add_environment(const char* key, const char* val);
diff --git a/init/parser.cpp b/init/parser.cpp
index 8193729..ae103ec 100644
--- a/init/parser.cpp
+++ b/init/parser.cpp
@@ -12,7 +12,7 @@
char buf[128];
int off;
- snprintf(buf, 128, "%s: %d: ", state->filename, state->line);
+ snprintf(buf, sizeof(buf), "%s: %d: ", state->filename, state->line);
buf[127] = 0;
off = strlen(buf);
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 5c1ae79..8c95f15 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -175,11 +175,7 @@
if (!is_legal_property_name(name, namelen)) return -1;
if (valuelen >= PROP_VALUE_MAX) return -1;
- if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) {
- if (selinux_reload_policy() != 0) {
- ERROR("Failed to reload policy\n");
- }
- } else if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
+ if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
if (restorecon_recursive(value) != 0) {
ERROR("Failed to restorecon_recursive %s\n", value);
}
diff --git a/init/readme.txt b/init/readme.txt
index ef85ccf..7d453c6 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -115,6 +115,11 @@
Options are modifiers to services. They affect how and when init
runs the service.
+console [<console>]
+ This service needs a console. The optional second parameter chooses a
+ specific console instead of the default "/dev/console". The leading "/dev/"
+ should be omitted, so "/dev/tty0" would be specified as just "console tty0".
+
critical
This is a device-critical service. If it exits more than four times in
four minutes, the device will reboot into recovery mode.
diff --git a/init/service.cpp b/init/service.cpp
index bdecc32..e7bbf7b 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -172,6 +172,7 @@
bool Service::HandleConsole(const std::vector<std::string>& args, std::string* err) {
flags_ |= SVC_CONSOLE;
+ console_ = args.size() > 1 ? args[1] : DEFAULT_CONSOLE;
return true;
}
@@ -282,7 +283,7 @@
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
static const Map option_handlers = {
{"class", {1, 1, &Service::HandleClass}},
- {"console", {0, 0, &Service::HandleConsole}},
+ {"console", {0, 1, &Service::HandleConsole}},
{"critical", {0, 0, &Service::HandleCritical}},
{"disabled", {0, 0, &Service::HandleDisabled}},
{"group", {1, NR_SVC_SUPP_GIDS + 1, &Service::HandleGroup}},
@@ -329,7 +330,7 @@
}
bool needs_console = (flags_ & SVC_CONSOLE);
- if (needs_console && !have_console) {
+ if (needs_console && console_names.empty()) {
ERROR("service '%s' requires console\n", name_.c_str());
flags_ |= SVC_DISABLED;
return false;
@@ -606,10 +607,12 @@
}
void Service::OpenConsole() const {
- int fd;
- if ((fd = open(console_name.c_str(), O_RDWR)) < 0) {
- fd = open("/dev/null", O_RDWR);
+ int fd = -1;
+ if (std::find(console_names.begin(), console_names.end(), console_) != console_names.end()) {
+ std::string c_path = "/dev/" + console_;
+ fd = open(c_path.c_str(), O_RDWR);
}
+ if (fd == -1) fd = open("/dev/null", O_RDWR);
ioctl(fd, TIOCSCTTY, 0);
dup2(fd, 0);
dup2(fd, 1);
diff --git a/init/service.h b/init/service.h
index 35abde9..b003ca0 100644
--- a/init/service.h
+++ b/init/service.h
@@ -129,6 +129,7 @@
std::string name_;
std::string classname_;
+ std::string console_;
unsigned flags_;
pid_t pid_;
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index baa3d0f..995abc0 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -148,3 +148,24 @@
return new UnwindPtrace(pid, tid, map);
}
}
+
+std::string Backtrace::GetErrorString(BacktraceUnwindError error) {
+ switch (error) {
+ case BACKTRACE_UNWIND_NO_ERROR:
+ return "No error";
+ case BACKTRACE_UNWIND_ERROR_SETUP_FAILED:
+ return "Setup failed";
+ case BACKTRACE_UNWIND_ERROR_MAP_MISSING:
+ return "No map found";
+ case BACKTRACE_UNWIND_ERROR_INTERNAL:
+ return "Internal libbacktrace error, please submit a bugreport";
+ case BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST:
+ return "Thread doesn't exist";
+ case BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT:
+ return "Thread has not repsonded to signal in time";
+ case BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION:
+ return "Attempt to use an unsupported feature";
+ case BACKTRACE_UNWIND_ERROR_NO_CONTEXT:
+ return "Attempt to do an offline unwind without a context";
+ }
+}
diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp
index 8e22366..5173e2c 100644
--- a/libbacktrace/BacktraceCurrent.cpp
+++ b/libbacktrace/BacktraceCurrent.cpp
@@ -24,6 +24,8 @@
#include <ucontext.h>
#include <unistd.h>
+#include <stdlib.h>
+
#include <string>
#include <backtrace/Backtrace.h>
@@ -65,9 +67,11 @@
bool BacktraceCurrent::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
if (GetMap() == nullptr) {
// Without a map object, we can't do anything.
+ error_ = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
return false;
}
+ error_ = BACKTRACE_UNWIND_NO_ERROR;
if (ucontext) {
return UnwindFromContext(num_ignore_frames, ucontext);
}
@@ -138,11 +142,19 @@
BACK_LOGE("sigaction failed: %s", strerror(errno));
ThreadEntry::Remove(entry);
pthread_mutex_unlock(&g_sigaction_mutex);
+ error_ = BACKTRACE_UNWIND_ERROR_INTERNAL;
return false;
}
if (tgkill(Pid(), Tid(), THREAD_SIGNAL) != 0) {
- BACK_LOGE("tgkill %d failed: %s", Tid(), strerror(errno));
+ // Do not emit an error message, this might be expected. Set the
+ // error and let the caller decide.
+ if (errno == ESRCH) {
+ error_ = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
+ } else {
+ error_ = BACKTRACE_UNWIND_ERROR_INTERNAL;
+ }
+
sigaction(THREAD_SIGNAL, &oldact, nullptr);
ThreadEntry::Remove(entry);
pthread_mutex_unlock(&g_sigaction_mutex);
@@ -183,7 +195,13 @@
BACK_LOGW("Timed out waiting for signal handler to indicate it finished.");
}
} else {
- BACK_LOGE("Timed out waiting for signal handler to get ucontext data.");
+ // Check to see if the thread has disappeared.
+ if (tgkill(Pid(), Tid(), 0) == -1 && errno == ESRCH) {
+ error_ = BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST;
+ } else {
+ error_ = BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT;
+ BACK_LOGE("Timed out waiting for signal handler to get ucontext data.");
+ }
}
ThreadEntry::Remove(entry);
diff --git a/libbacktrace/BacktraceOffline.cpp b/libbacktrace/BacktraceOffline.cpp
index c10634b..dc3ed67 100644
--- a/libbacktrace/BacktraceOffline.cpp
+++ b/libbacktrace/BacktraceOffline.cpp
@@ -129,9 +129,11 @@
bool BacktraceOffline::Unwind(size_t num_ignore_frames, ucontext_t* context) {
if (context == nullptr) {
BACK_LOGW("The context is needed for offline backtracing.");
+ error_ = BACKTRACE_UNWIND_ERROR_NO_CONTEXT;
return false;
}
context_ = context;
+ error_ = BACKTRACE_UNWIND_NO_ERROR;
unw_addr_space_t addr_space = unw_create_addr_space(&accessors, 0);
unw_cursor_t cursor;
@@ -139,6 +141,7 @@
if (ret != 0) {
BACK_LOGW("unw_init_remote failed %d", ret);
unw_destroy_addr_space(addr_space);
+ error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
return false;
}
size_t num_frames = 0;
diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp
index 67e583f..666c481 100644
--- a/libbacktrace/UnwindCurrent.cpp
+++ b/libbacktrace/UnwindCurrent.cpp
@@ -70,6 +70,7 @@
int ret = unw_getcontext(&context_);
if (ret < 0) {
BACK_LOGW("unw_getcontext failed %d", ret);
+ error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
return false;
}
} else {
@@ -81,6 +82,7 @@
int ret = unw_init_local(cursor.get(), &context_);
if (ret < 0) {
BACK_LOGW("unw_init_local failed %d", ret);
+ error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
return false;
}
diff --git a/libbacktrace/UnwindPtrace.cpp b/libbacktrace/UnwindPtrace.cpp
index 07c2430..306d2ac 100644
--- a/libbacktrace/UnwindPtrace.cpp
+++ b/libbacktrace/UnwindPtrace.cpp
@@ -50,17 +50,22 @@
bool UnwindPtrace::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) {
if (GetMap() == nullptr) {
// Without a map object, we can't do anything.
+ error_ = BACKTRACE_UNWIND_ERROR_MAP_MISSING;
return false;
}
+ error_ = BACKTRACE_UNWIND_NO_ERROR;
+
if (ucontext) {
BACK_LOGW("Unwinding from a specified context not supported yet.");
+ error_ = BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION;
return false;
}
addr_space_ = unw_create_addr_space(&_UPT_accessors, 0);
if (!addr_space_) {
BACK_LOGW("unw_create_addr_space failed.");
+ error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
return false;
}
@@ -70,6 +75,7 @@
upt_info_ = reinterpret_cast<struct UPT_info*>(_UPT_create(Tid()));
if (!upt_info_) {
BACK_LOGW("Failed to create upt info.");
+ error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
return false;
}
@@ -77,6 +83,7 @@
int ret = unw_init_remote(&cursor, addr_space_, upt_info_);
if (ret < 0) {
BACK_LOGW("unw_init_remote failed %d", ret);
+ error_ = BACKTRACE_UNWIND_ERROR_SETUP_FAILED;
return false;
}
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index b975db9..df6c6c1 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -162,6 +162,7 @@
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
VerifyLevelDump(backtrace.get());
}
@@ -183,6 +184,7 @@
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
VerifyMaxDump(backtrace.get());
}
@@ -200,6 +202,7 @@
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), tid));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
VerifyFunc(backtrace.get());
}
@@ -220,6 +223,7 @@
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), getpid()));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
ASSERT_TRUE(backtrace->NumFrames() != 0);
for (const auto& frame : *backtrace ) {
@@ -267,16 +271,19 @@
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(all.get() != nullptr);
ASSERT_TRUE(all->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, all->GetError());
std::unique_ptr<Backtrace> ign1(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(ign1.get() != nullptr);
ASSERT_TRUE(ign1->Unwind(1));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError());
std::unique_ptr<Backtrace> ign2(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(ign2.get() != nullptr);
ASSERT_TRUE(ign2->Unwind(2));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError());
VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), "VerifyLevelIgnoreFrames");
}
@@ -314,6 +321,7 @@
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map.get()));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
if (ReadyFunc(backtrace.get())) {
VerifyFunc(backtrace.get());
verified = true;
@@ -372,10 +380,12 @@
std::unique_ptr<Backtrace> ign1(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(ign1.get() != nullptr);
ASSERT_TRUE(ign1->Unwind(1));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError());
std::unique_ptr<Backtrace> ign2(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(ign2.get() != nullptr);
ASSERT_TRUE(ign2->Unwind(2));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError());
VerifyIgnoreFrames(bt_all, ign1.get(), ign2.get(), nullptr);
}
@@ -462,6 +472,7 @@
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
VerifyLevelDump(backtrace.get());
}
@@ -474,6 +485,7 @@
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), gettid()));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
VerifyMaxDump(backtrace.get());
}
@@ -515,6 +527,7 @@
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
VerifyLevelDump(backtrace.get());
@@ -554,14 +567,17 @@
std::unique_ptr<Backtrace> all(Backtrace::Create(getpid(), thread_data.tid));
ASSERT_TRUE(all.get() != nullptr);
ASSERT_TRUE(all->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, all->GetError());
std::unique_ptr<Backtrace> ign1(Backtrace::Create(getpid(), thread_data.tid));
ASSERT_TRUE(ign1.get() != nullptr);
ASSERT_TRUE(ign1->Unwind(1));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError());
std::unique_ptr<Backtrace> ign2(Backtrace::Create(getpid(), thread_data.tid));
ASSERT_TRUE(ign2.get() != nullptr);
ASSERT_TRUE(ign2->Unwind(2));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError());
VerifyIgnoreFrames(all.get(), ign1.get(), ign2.get(), nullptr);
@@ -592,6 +608,7 @@
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(getpid(), thread_data.tid));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
VerifyMaxDump(backtrace.get());
@@ -718,18 +735,21 @@
Backtrace* back1 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map1);
ASSERT_TRUE(back1 != nullptr);
EXPECT_TRUE(back1->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back1->GetError());
delete back1;
delete map1;
Backtrace* back2 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map2);
ASSERT_TRUE(back2 != nullptr);
EXPECT_TRUE(back2->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back2->GetError());
delete back2;
delete map2;
Backtrace* back3 = Backtrace::Create(getpid(), BACKTRACE_CURRENT_THREAD, map3);
ASSERT_TRUE(back3 != nullptr);
EXPECT_TRUE(back3->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, back3->GetError());
delete back3;
delete map3;
}
@@ -1308,6 +1328,7 @@
BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
size_t frame_num;
ASSERT_TRUE(FindFuncFrameInBacktrace(backtrace.get(), test_func, &frame_num));
@@ -1364,6 +1385,7 @@
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD));
ASSERT_TRUE(backtrace.get() != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
size_t frame_num;
if (FindFuncFrameInBacktrace(backtrace.get(),
@@ -1387,6 +1409,14 @@
ASSERT_TRUE(done) << "Test function never found in unwind.";
}
+TEST(libbacktrace, unwind_thread_doesnt_exist) {
+ std::unique_ptr<Backtrace> backtrace(
+ Backtrace::Create(BACKTRACE_CURRENT_PROCESS, 99999999));
+ ASSERT_TRUE(backtrace.get() != nullptr);
+ ASSERT_FALSE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST, backtrace->GetError());
+}
+
#if defined(ENABLE_PSS_TESTS)
#include "GetPss.h"
@@ -1398,6 +1428,7 @@
Backtrace* backtrace = Backtrace::Create(pid, tid);
ASSERT_TRUE(backtrace != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
delete backtrace;
}
size_t stable_pss = GetPssBytes();
@@ -1408,13 +1439,14 @@
Backtrace* backtrace = Backtrace::Create(pid, tid);
ASSERT_TRUE(backtrace != nullptr);
ASSERT_TRUE(backtrace->Unwind(0));
+ ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
delete backtrace;
}
size_t new_pss = GetPssBytes();
ASSERT_TRUE(new_pss != 0);
- size_t abs_diff = (new_pss > stable_pss) ? new_pss - stable_pss : stable_pss - new_pss;
- // As long as the new pss is within a certain amount, consider everything okay.
- ASSERT_LE(abs_diff, MAX_LEAK_BYTES);
+ if (new_pss > stable_pss) {
+ ASSERT_LE(new_pss - stable_pss, MAX_LEAK_BYTES);
+ }
}
TEST(libbacktrace, check_for_leak_local) {
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index 51c6d9d..c0d4d76 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -121,6 +121,9 @@
ifneq ($(ENABLE_CPUSETS),)
LOCAL_CFLAGS += -DUSE_CPUSETS
endif
+ifneq ($(ENABLE_SCHEDBOOST),)
+LOCAL_CFLAGS += -DUSE_SCHEDBOOST
+endif
LOCAL_CFLAGS += -Werror -Wall -Wextra -std=gnu90
LOCAL_CLANG := true
LOCAL_SANITIZE := integer
@@ -135,6 +138,9 @@
ifneq ($(ENABLE_CPUSETS),)
LOCAL_CFLAGS += -DUSE_CPUSETS
endif
+ifneq ($(ENABLE_SCHEDBOOST),)
+LOCAL_CFLAGS += -DUSE_SCHEDBOOST
+endif
LOCAL_CFLAGS += -Werror -Wall -Wextra
LOCAL_C_INCLUDES := $(libcutils_c_includes)
LOCAL_CLANG := true
diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c
index 298e3da..6bba3a7 100644
--- a/libcutils/sched_policy.c
+++ b/libcutils/sched_policy.c
@@ -64,6 +64,8 @@
// File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error
static int bg_cpuset_fd = -1;
static int fg_cpuset_fd = -1;
+static int bg_schedboost_fd = -1;
+static int fg_schedboost_fd = -1;
#endif
/* Add tid to the scheduling group defined by the policy */
@@ -128,6 +130,12 @@
fg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
filename = "/dev/cpuset/background/tasks";
bg_cpuset_fd = open(filename, O_WRONLY | O_CLOEXEC);
+#ifdef USE_SCHEDBOOST
+ filename = "/sys/fs/cgroup/stune/foreground/tasks";
+ fg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+ filename = "/sys/fs/cgroup/stune/tasks";
+ bg_schedboost_fd = open(filename, O_WRONLY | O_CLOEXEC);
+#endif
}
#endif
@@ -253,17 +261,20 @@
pthread_once(&the_once, __initialize);
int fd;
+ int boost_fd;
switch (policy) {
case SP_BACKGROUND:
fd = bg_cpuset_fd;
+ boost_fd = bg_schedboost_fd;
break;
case SP_FOREGROUND:
case SP_AUDIO_APP:
case SP_AUDIO_SYS:
fd = fg_cpuset_fd;
+ boost_fd = fg_schedboost_fd;
break;
default:
- fd = -1;
+ boost_fd = fd = -1;
break;
}
@@ -272,6 +283,11 @@
return -errno;
}
+ if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
+ if (errno != ESRCH && errno != ENOENT)
+ return -errno;
+ }
+
return 0;
#endif
}
diff --git a/libion/tests/Android.mk b/libion/tests/Android.mk
index 894f90e..d4d07c2 100644
--- a/libion/tests/Android.mk
+++ b/libion/tests/Android.mk
@@ -17,8 +17,9 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
+LOCAL_CLANG := true
LOCAL_MODULE := ion-unit-tests
-LOCAL_CFLAGS += -g -Wall -Werror -std=gnu++11 -Wno-missing-field-initializers
+LOCAL_CFLAGS += -g -Wall -Werror -Wno-missing-field-initializers
LOCAL_SHARED_LIBRARIES += libion
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../kernel-headers
LOCAL_SRC_FILES := \
diff --git a/libion/tests/allocate_test.cpp b/libion/tests/allocate_test.cpp
index e26b302..3c4524e 100644
--- a/libion/tests/allocate_test.cpp
+++ b/libion/tests/allocate_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <memory>
#include <sys/mman.h>
#include <gtest/gtest.h>
@@ -90,7 +91,7 @@
TEST_F(Allocate, Zeroed)
{
- void *zeroes = calloc(4096, 1);
+ auto zeroes_ptr = std::make_unique<char[]>(4096);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -125,14 +126,11 @@
ptr = mmap(NULL, 4096, PROT_READ, MAP_SHARED, map_fd, 0);
ASSERT_TRUE(ptr != NULL);
- ASSERT_EQ(0, memcmp(ptr, zeroes, 4096));
+ ASSERT_EQ(0, memcmp(ptr, zeroes_ptr.get(), 4096));
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(zeroes);
-
}
TEST_F(Allocate, Large)
diff --git a/libion/tests/device_test.cpp b/libion/tests/device_test.cpp
index 6f6e1bd..0be52bf 100644
--- a/libion/tests/device_test.cpp
+++ b/libion/tests/device_test.cpp
@@ -15,6 +15,7 @@
*/
#include <fcntl.h>
+#include <memory>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -133,8 +134,8 @@
TEST_F(Device, KernelReadCached)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -161,14 +162,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, KernelWriteCached)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (int i = 0; i < 4096; i++)
((char *)buf)[i] = i;
@@ -195,14 +194,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, DMAReadCached)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -227,14 +224,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, DMAWriteCached)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (int i = 0; i < 4096; i++)
((char *)buf)[i] = i;
@@ -261,14 +256,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, KernelReadCachedNeedsSync)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -295,14 +288,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, KernelWriteCachedNeedsSync)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (int i = 0; i < 4096; i++)
((char *)buf)[i] = i;
@@ -329,14 +320,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, DMAReadCachedNeedsSync)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -363,14 +352,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, DMAWriteCachedNeedsSync)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (int i = 0; i < 4096; i++)
((char *)buf)[i] = i;
@@ -399,13 +386,11 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, KernelRead)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -432,14 +417,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, KernelWrite)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (int i = 0; i < 4096; i++)
((char *)buf)[i] = i;
@@ -466,14 +449,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, DMARead)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
@@ -498,14 +479,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, DMAWrite)
{
- void *alloc = malloc(8192 + 1024);
- void *buf = (void *)(ALIGN((unsigned long)alloc, 4096) + 1024);
+ auto alloc_ptr = std::make_unique<char[]>(8192 + 1024);
+ void *buf = (void *)(ALIGN((unsigned long)alloc_ptr.get(), 4096) + 1024);
for (int i = 0; i < 4096; i++)
((char *)buf)[i] = i;
@@ -532,13 +511,12 @@
ASSERT_EQ(0, munmap(ptr, 4096));
ASSERT_EQ(0, close(map_fd));
}
-
- free(alloc);
}
TEST_F(Device, IsCached)
{
- void *buf = malloc(4096);
+ auto buf_ptr = std::make_unique<char[]>(4096);
+ void *buf = buf_ptr.get();
for (unsigned int heapMask : m_allHeaps) {
SCOPED_TRACE(::testing::Message() << "heap " << heapMask);
diff --git a/liblog/Android.mk b/liblog/Android.mk
index dd5b518..6d53a4a 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -39,7 +39,7 @@
LOCAL_SRC_FILES_darwin := event_tag_map.c
LOCAL_SRC_FILES_linux := event_tag_map.c
LOCAL_SRC_FILES_windows := uio.c
-LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -Werror $(liblog_cflags)
+LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -Werror -fvisibility=hidden $(liblog_cflags)
LOCAL_MULTILIB := both
LOCAL_MODULE_HOST_OS := darwin linux windows
include $(BUILD_HOST_STATIC_LIBRARY)
@@ -59,7 +59,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE := liblog
LOCAL_SRC_FILES := $(liblog_target_sources)
-LOCAL_CFLAGS := -Werror $(liblog_cflags)
+LOCAL_CFLAGS := -Werror -fvisibility=hidden $(liblog_cflags)
# AddressSanitizer runtime library depends on liblog.
LOCAL_SANITIZE := never
include $(BUILD_STATIC_LIBRARY)
@@ -67,7 +67,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE := liblog
LOCAL_WHOLE_STATIC_LIBRARIES := liblog
-LOCAL_CFLAGS := -Werror $(liblog_cflags)
+LOCAL_CFLAGS := -Werror -fvisibility=hidden $(liblog_cflags)
# TODO: This is to work around b/24465209. Remove after root cause is fixed
LOCAL_LDFLAGS_arm := -Wl,--hash-style=both
diff --git a/liblog/event_tag_map.c b/liblog/event_tag_map.c
index bea99aa..870c69a 100644
--- a/liblog/event_tag_map.c
+++ b/liblog/event_tag_map.c
@@ -24,6 +24,8 @@
#include <log/event_tag_map.h>
#include <log/log.h>
+#include "log_cdefs.h"
+
#define OUT_TAG "EventTagMap"
/*
@@ -61,7 +63,7 @@
* We create a private mapping because we want to terminate the log tag
* strings with '\0'.
*/
-EventTagMap* android_openEventTagMap(const char* fileName)
+LIBLOG_ABI_PUBLIC EventTagMap* android_openEventTagMap(const char* fileName)
{
EventTagMap* newTagMap;
off_t end;
@@ -109,7 +111,7 @@
/*
* Close the map.
*/
-void android_closeEventTagMap(EventTagMap* map)
+LIBLOG_ABI_PUBLIC void android_closeEventTagMap(EventTagMap* map)
{
if (map == NULL)
return;
@@ -123,7 +125,8 @@
*
* The entries are sorted by tag number, so we can do a binary search.
*/
-const char* android_lookupEventTag(const EventTagMap* map, int tag)
+LIBLOG_ABI_PUBLIC const char* android_lookupEventTag(const EventTagMap* map,
+ int tag)
{
int hi, lo, mid;
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index aa88bed..c73e03e 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -19,23 +19,19 @@
* passed on to the underlying (fake) log device. When not in the
* simulator, messages are printed to stderr.
*/
-#include "fake_log_device.h"
-
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
+#if !defined(_WIN32)
+#include <pthread.h>
+#endif
#include <stdlib.h>
#include <string.h>
#include <log/logd.h>
-#if !defined(_WIN32)
-#include <pthread.h>
-#endif
-
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
+#include "fake_log_device.h"
+#include "log_cdefs.h"
#define kMaxTagLen 16 /* from the long-dead utils/Log.cpp */
@@ -512,7 +508,7 @@
}
numLines -= 1;
}
-
+
/*
* Write the entire message to the log file with a single writev() call.
* We need to use this rather than a collection of printf()s on a FILE*
@@ -531,10 +527,10 @@
int cc = writev(fileno(stderr), vec, v-vec);
if (cc == totalLen) break;
-
+
if (cc < 0) {
if(errno == EINTR) continue;
-
+
/* can't really log the failure; for now, throw out a stderr */
fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
break;
@@ -683,7 +679,7 @@
}
}
-int fakeLogOpen(const char *pathName, int flags)
+LIBLOG_HIDDEN int fakeLogOpen(const char *pathName, int flags)
{
if (redirectOpen == NULL) {
setRedirects();
@@ -702,19 +698,22 @@
* call is in the exit handler. Logging can continue in the exit handler to
* help debug HOST tools ...
*/
-int fakeLogClose(int fd)
+LIBLOG_HIDDEN int fakeLogClose(int fd)
{
/* Assume that open() was called first. */
return redirectClose(fd);
}
-ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count)
+LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd,
+ const struct iovec* vector, int count)
{
/* Assume that open() was called first. */
return redirectWritev(fd, vector, count);
}
-int __android_log_is_loggable(int prio, const char *tag __unused, int def)
+LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio,
+ const char *tag __unused,
+ int def)
{
int logLevel = def;
return logLevel >= 0 && prio >= logLevel;
diff --git a/liblog/fake_log_device.h b/liblog/fake_log_device.h
index 9d168cd..672b446 100644
--- a/liblog/fake_log_device.h
+++ b/liblog/fake_log_device.h
@@ -19,10 +19,13 @@
#include <sys/types.h>
+#include "log_cdefs.h"
+
struct iovec;
-int fakeLogOpen(const char *pathName, int flags);
-int fakeLogClose(int fd);
-ssize_t fakeLogWritev(int fd, const struct iovec* vector, int count);
+LIBLOG_HIDDEN int fakeLogOpen(const char *pathName, int flags);
+LIBLOG_HIDDEN int fakeLogClose(int fd);
+LIBLOG_HIDDEN ssize_t fakeLogWritev(int fd,
+ const struct iovec* vector, int count);
#endif // _LIBLOG_FAKE_LOG_DEVICE_H
diff --git a/liblog/log_cdefs.h b/liblog/log_cdefs.h
new file mode 100644
index 0000000..3a52625
--- /dev/null
+++ b/liblog/log_cdefs.h
@@ -0,0 +1,54 @@
+/*
+ * 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 _LIBLOG_CDEFS_H__
+#define _LIBLOG_CDEFS_H__
+
+#include <sys/cdefs.h>
+
+/* Declare this library function hidden and internal */
+#if defined(_WIN32)
+#define LIBLOG_HIDDEN
+#else
+#define LIBLOG_HIDDEN __attribute__((visibility("hidden")))
+#endif
+
+/* Declare this library function visible and external */
+#if defined(_WIN32)
+#define LIBLOG_ABI_PUBLIC
+#else
+#define LIBLOG_ABI_PUBLIC __attribute__((visibility("default")))
+#endif
+
+/* Declare this library function visible but private */
+#define LIBLOG_ABI_PRIVATE LIBLOG_ABI_PUBLIC
+
+/*
+ * Declare this library function as reimplementation.
+ * Prevent circular dependencies, but allow _real_ library to hijack
+ */
+#if defined(_WIN32)
+#define LIBLOG_WEAK static /* Accept that it is totally private */
+#else
+#define LIBLOG_WEAK __attribute__((weak,visibility("default")))
+#endif
+
+/* Unused argument. For C code only, remove symbol name for C++ */
+#ifndef __unused
+#define __unused __attribute__((__unused__))
+#endif
+
+#endif /* _LIBLOG_CDEFS_H__ */
diff --git a/liblog/log_event_list.c b/liblog/log_event_list.c
index 2213f21..a77c56e 100644
--- a/liblog/log_event_list.c
+++ b/liblog/log_event_list.c
@@ -25,6 +25,8 @@
#include <log/log.h>
#include <log/logger.h>
+#include "log_cdefs.h"
+
#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
typedef struct {
@@ -43,7 +45,7 @@
uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
} android_log_context_internal;
-android_log_context create_android_logger(uint32_t tag) {
+LIBLOG_ABI_PUBLIC android_log_context create_android_logger(uint32_t tag) {
size_t needed, i;
android_log_context_internal *context;
@@ -65,7 +67,9 @@
return (android_log_context)context;
}
-android_log_context create_android_log_parser(const char *msg, size_t len) {
+LIBLOG_ABI_PUBLIC android_log_context create_android_log_parser(
+ const char *msg,
+ size_t len) {
android_log_context_internal *context;
size_t i;
@@ -81,7 +85,7 @@
return (android_log_context)context;
}
-int android_log_destroy(android_log_context *ctx) {
+LIBLOG_ABI_PUBLIC int android_log_destroy(android_log_context *ctx) {
android_log_context_internal *context;
context = (android_log_context_internal *)*ctx;
@@ -94,7 +98,7 @@
return 0;
}
-int android_log_write_list_begin(android_log_context ctx) {
+LIBLOG_ABI_PUBLIC int android_log_write_list_begin(android_log_context ctx) {
size_t needed;
android_log_context_internal *context;
@@ -137,7 +141,8 @@
buf[3] = (val >> 24) & 0xFF;
}
-int android_log_write_int32(android_log_context ctx, int32_t value) {
+LIBLOG_ABI_PUBLIC int android_log_write_int32(android_log_context ctx,
+ int32_t value) {
size_t needed;
android_log_context_internal *context;
@@ -172,7 +177,8 @@
buf[7] = (val >> 56) & 0xFF;
}
-int android_log_write_int64(android_log_context ctx, int64_t value) {
+LIBLOG_ABI_PUBLIC int android_log_write_int64(android_log_context ctx,
+ int64_t value) {
size_t needed;
android_log_context_internal *context;
@@ -195,8 +201,9 @@
return 0;
}
-int android_log_write_string8_len(android_log_context ctx,
- const char *value, size_t maxlen) {
+LIBLOG_ABI_PUBLIC int android_log_write_string8_len(android_log_context ctx,
+ const char *value,
+ size_t maxlen) {
size_t needed;
ssize_t len;
android_log_context_internal *context;
@@ -231,11 +238,13 @@
return len;
}
-int android_log_write_string8(android_log_context ctx, const char *value) {
+LIBLOG_ABI_PUBLIC int android_log_write_string8(android_log_context ctx,
+ const char *value) {
return android_log_write_string8_len(ctx, value, MAX_EVENT_PAYLOAD);
}
-int android_log_write_float32(android_log_context ctx, float value) {
+LIBLOG_ABI_PUBLIC int android_log_write_float32(android_log_context ctx,
+ float value) {
size_t needed;
uint32_t ivalue;
android_log_context_internal *context;
@@ -260,7 +269,7 @@
return 0;
}
-int android_log_write_list_end(android_log_context ctx) {
+LIBLOG_ABI_PUBLIC int android_log_write_list_end(android_log_context ctx) {
android_log_context_internal *context;
context = (android_log_context_internal *)ctx;
@@ -290,7 +299,8 @@
/*
* Logs the list of elements to the event log.
*/
-int android_log_write_list(android_log_context ctx, log_id_t id) {
+LIBLOG_ABI_PUBLIC int android_log_write_list(android_log_context ctx,
+ log_id_t id) {
android_log_context_internal *context;
const char *msg;
ssize_t len;
@@ -518,10 +528,12 @@
}
}
-android_log_list_element android_log_read_next(android_log_context ctx) {
+LIBLOG_ABI_PUBLIC android_log_list_element android_log_read_next(
+ android_log_context ctx) {
return android_log_read_next_internal(ctx, 0);
}
-android_log_list_element android_log_peek_next(android_log_context ctx) {
+LIBLOG_ABI_PUBLIC android_log_list_element android_log_peek_next(
+ android_log_context ctx) {
return android_log_read_next_internal(ctx, 1);
}
diff --git a/liblog/log_event_write.c b/liblog/log_event_write.c
index ad42edd..3535b94 100644
--- a/liblog/log_event_write.c
+++ b/liblog/log_event_write.c
@@ -18,10 +18,15 @@
#include <log/log.h>
+#include "log_cdefs.h"
+
#define MAX_SUBTAG_LEN 32
-int __android_log_error_write(int tag, const char *subTag, int32_t uid,
- const char *data, uint32_t dataLen)
+LIBLOG_ABI_PUBLIC int __android_log_error_write(
+ int tag,
+ const char *subTag,
+ int32_t uid,
+ const char *data, uint32_t dataLen)
{
int ret = -EINVAL;
diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c
index 2f8f886..47fde20 100644
--- a/liblog/log_is_loggable.c
+++ b/liblog/log_is_loggable.c
@@ -23,6 +23,8 @@
#include <android/log.h>
+#include "log_cdefs.h"
+
static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER;
static int lock()
@@ -250,7 +252,8 @@
return default_prio;
}
-int __android_log_is_loggable(int prio, const char *tag, int default_prio)
+LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio, const char *tag,
+ int default_prio)
{
int logLevel = __android_log_level(tag, default_prio);
return logLevel >= 0 && prio >= logLevel;
@@ -315,7 +318,7 @@
* Timestamp state generally remains constant, but can change at any time
* to handle developer requirements.
*/
-clockid_t android_log_clockid()
+LIBLOG_ABI_PUBLIC clockid_t android_log_clockid()
{
static struct cache2 clockid = {
PTHREAD_MUTEX_INITIALIZER,
@@ -343,7 +346,7 @@
return (c != BOOLEAN_FALSE) && c && (self->cache_persist.c == BOOLEAN_TRUE);
}
-int __android_log_security()
+LIBLOG_ABI_PUBLIC int __android_log_security()
{
static struct cache2 security = {
PTHREAD_MUTEX_INITIALIZER,
diff --git a/liblog/log_read.c b/liblog/log_read.c
index fc63d2c..4b83944 100644
--- a/liblog/log_read.c
+++ b/liblog/log_read.c
@@ -24,7 +24,6 @@
#define NOMINMAX /* for windows to suppress definition of min in stdlib.h */
#include <stdlib.h>
#include <string.h>
-#include <sys/cdefs.h>
#include <unistd.h>
#include <cutils/list.h>
@@ -34,23 +33,16 @@
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
+#include "log_cdefs.h"
+
/* branchless on many architectures. */
#define min(x,y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
-#if defined(_WIN32)
-#define WEAK static
-#else
-#define WEAK __attribute__((weak))
-#endif
-#ifndef __unused
-#define __unused __attribute__((unused))
-#endif
-
/* Private copy of ../libcutils/socket_local_client.c prevent library loops */
#if defined(_WIN32)
-int WEAK socket_local_client(const char *name, int namespaceId, int type)
+LIBLOG_WEAK int socket_local_client(const char *name, int namespaceId, int type)
{
errno = ENOSYS;
return -ENOSYS;
@@ -71,8 +63,9 @@
#define LISTEN_BACKLOG 4
/* Documented in header file. */
-int WEAK socket_make_sockaddr_un(const char *name, int namespaceId,
- struct sockaddr_un *p_addr, socklen_t *alen)
+LIBLOG_WEAK int socket_make_sockaddr_un(const char *name, int namespaceId,
+ struct sockaddr_un *p_addr,
+ socklen_t *alen)
{
memset (p_addr, 0, sizeof (*p_addr));
size_t namelen;
@@ -151,8 +144,8 @@
*
* Used by AndroidSocketImpl
*/
-int WEAK socket_local_client_connect(int fd, const char *name, int namespaceId,
- int type __unused)
+LIBLOG_WEAK int socket_local_client_connect(int fd, const char *name,
+ int namespaceId, int type __unused)
{
struct sockaddr_un addr;
socklen_t alen;
@@ -178,7 +171,7 @@
* connect to peer named "name"
* returns fd or -1 on error
*/
-int WEAK socket_local_client(const char *name, int namespaceId, int type)
+LIBLOG_WEAK int socket_local_client(const char *name, int namespaceId, int type)
{
int s;
@@ -212,7 +205,7 @@
[LOG_ID_KERNEL] = "kernel",
};
-const char *android_log_id_to_name(log_id_t log_id)
+LIBLOG_ABI_PUBLIC const char *android_log_id_to_name(log_id_t log_id)
{
if (log_id >= LOG_ID_MAX) {
log_id = LOG_ID_MAIN;
@@ -220,7 +213,7 @@
return LOG_NAME[log_id];
}
-log_id_t android_name_to_log_id(const char *logName)
+LIBLOG_ABI_PUBLIC log_id_t android_name_to_log_id(const char *logName)
{
const char *b;
int ret;
@@ -275,7 +268,7 @@
/* android_logger_alloc unimplemented, no use case */
/* method for getting the associated sublog id */
-log_id_t android_logger_get_id(struct logger *logger)
+LIBLOG_ABI_PUBLIC log_id_t android_logger_get_id(struct logger *logger)
{
return logger->id;
}
@@ -409,7 +402,7 @@
return last_uid = uid;
}
-int android_logger_clear(struct logger *logger)
+LIBLOG_ABI_PUBLIC int android_logger_clear(struct logger *logger)
{
char buf[512];
@@ -425,7 +418,7 @@
}
/* returns the total size of the log's ring buffer */
-long android_logger_get_log_size(struct logger *logger)
+LIBLOG_ABI_PUBLIC long android_logger_get_log_size(struct logger *logger)
{
char buf[512];
@@ -441,7 +434,8 @@
return atol(buf);
}
-int android_logger_set_log_size(struct logger *logger, unsigned long size)
+LIBLOG_ABI_PUBLIC int android_logger_set_log_size(struct logger *logger,
+ unsigned long size)
{
char buf[512];
@@ -455,7 +449,8 @@
* returns the readable size of the log's ring buffer (that is, amount of the
* log consumed)
*/
-long android_logger_get_log_readable_size(struct logger *logger)
+LIBLOG_ABI_PUBLIC long android_logger_get_log_readable_size(
+ struct logger *logger)
{
char buf[512];
@@ -474,16 +469,18 @@
/*
* returns the logger version
*/
-int android_logger_get_log_version(struct logger *logger __unused)
+LIBLOG_ABI_PUBLIC int android_logger_get_log_version(
+ struct logger *logger __unused)
{
- return 3;
+ return 4;
}
/*
* returns statistics
*/
-ssize_t android_logger_get_statistics(struct logger_list *logger_list,
- char *buf, size_t len)
+LIBLOG_ABI_PUBLIC ssize_t android_logger_get_statistics(
+ struct logger_list *logger_list,
+ char *buf, size_t len)
{
struct logger *logger;
char *cp = buf;
@@ -509,14 +506,16 @@
return send_log_msg(NULL, NULL, buf, len);
}
-ssize_t android_logger_get_prune_list(struct logger_list *logger_list __unused,
- char *buf, size_t len)
+LIBLOG_ABI_PUBLIC ssize_t android_logger_get_prune_list(
+ struct logger_list *logger_list __unused,
+ char *buf, size_t len)
{
return send_log_msg(NULL, "getPruneList", buf, len);
}
-int android_logger_set_prune_list(struct logger_list *logger_list __unused,
- char *buf, size_t len)
+LIBLOG_ABI_PUBLIC int android_logger_set_prune_list(
+ struct logger_list *logger_list __unused,
+ char *buf, size_t len)
{
const char cmd[] = "setPruneList ";
const size_t cmdlen = sizeof(cmd) - 1;
@@ -531,9 +530,10 @@
return check_log_success(buf, send_log_msg(NULL, NULL, buf, len));
}
-struct logger_list *android_logger_list_alloc(int mode,
- unsigned int tail,
- pid_t pid)
+LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_alloc(
+ int mode,
+ unsigned int tail,
+ pid_t pid)
{
struct logger_list *logger_list;
@@ -553,9 +553,10 @@
return logger_list;
}
-struct logger_list *android_logger_list_alloc_time(int mode,
- log_time start,
- pid_t pid)
+LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_alloc_time(
+ int mode,
+ log_time start,
+ pid_t pid)
{
struct logger_list *logger_list;
@@ -578,8 +579,9 @@
/* android_logger_list_unregister unimplemented, no use case */
/* Open the named log and add it to the logger list */
-struct logger *android_logger_open(struct logger_list *logger_list,
- log_id_t id)
+LIBLOG_ABI_PUBLIC struct logger *android_logger_open(
+ struct logger_list *logger_list,
+ log_id_t id)
{
struct logger *logger;
@@ -610,10 +612,11 @@
}
/* Open the single named log and make it part of a new logger list */
-struct logger_list *android_logger_list_open(log_id_t id,
- int mode,
- unsigned int tail,
- pid_t pid)
+LIBLOG_ABI_PUBLIC struct logger_list *android_logger_list_open(
+ log_id_t id,
+ int mode,
+ unsigned int tail,
+ pid_t pid)
{
struct logger_list *logger_list = android_logger_list_alloc(mode, tail, pid);
if (!logger_list) {
@@ -751,8 +754,9 @@
}
/* Read from the selected logs */
-int android_logger_list_read(struct logger_list *logger_list,
- struct log_msg *log_msg)
+LIBLOG_ABI_PUBLIC int android_logger_list_read(
+ struct logger_list *logger_list,
+ struct log_msg *log_msg)
{
int ret, e;
struct logger *logger;
@@ -892,7 +896,8 @@
}
/* Close all the logs */
-void android_logger_list_free(struct logger_list *logger_list)
+LIBLOG_ABI_PUBLIC void android_logger_list_free(
+ struct logger_list *logger_list)
{
if (logger_list == NULL) {
return;
diff --git a/liblog/log_time.cpp b/liblog/log_time.cpp
index 9d5ea0e..b6af222 100644
--- a/liblog/log_time.cpp
+++ b/liblog/log_time.cpp
@@ -18,16 +18,17 @@
#include <limits.h>
#include <stdio.h>
#include <string.h>
-#include <sys/cdefs.h>
#include <log/log_read.h>
-const char log_time::default_format[] = "%m-%d %H:%M:%S.%q";
-const timespec log_time::EPOCH = { 0, 0 };
+#include "log_cdefs.h"
+
+LIBLOG_ABI_PRIVATE const char log_time::default_format[] = "%m-%d %H:%M:%S.%q";
+LIBLOG_ABI_PRIVATE const timespec log_time::EPOCH = { 0, 0 };
// Add %#q for fractional seconds to standard strptime function
-char *log_time::strptime(const char *s, const char *format) {
+LIBLOG_ABI_PRIVATE char *log_time::strptime(const char *s, const char *format) {
time_t now;
#ifdef __linux__
*this = log_time(CLOCK_REALTIME);
@@ -133,7 +134,7 @@
return ret;
}
-log_time log_time::operator-= (const timespec &T) {
+LIBLOG_ABI_PRIVATE log_time log_time::operator-= (const timespec &T) {
// No concept of negative time, clamp to EPOCH
if (*this <= T) {
return *this = EPOCH;
@@ -150,7 +151,7 @@
return *this;
}
-log_time log_time::operator+= (const timespec &T) {
+LIBLOG_ABI_PRIVATE log_time log_time::operator+= (const timespec &T) {
this->tv_nsec += (unsigned long int)T.tv_nsec;
if (this->tv_nsec >= NS_PER_SEC) {
this->tv_nsec -= NS_PER_SEC;
@@ -161,7 +162,7 @@
return *this;
}
-log_time log_time::operator-= (const log_time &T) {
+LIBLOG_ABI_PRIVATE log_time log_time::operator-= (const log_time &T) {
// No concept of negative time, clamp to EPOCH
if (*this <= T) {
return *this = EPOCH;
@@ -178,7 +179,7 @@
return *this;
}
-log_time log_time::operator+= (const log_time &T) {
+LIBLOG_ABI_PRIVATE log_time log_time::operator+= (const log_time &T) {
this->tv_nsec += T.tv_nsec;
if (this->tv_nsec >= NS_PER_SEC) {
this->tv_nsec -= NS_PER_SEC;
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index b173d1a..85a4aab 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -46,6 +46,8 @@
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
+#include "log_cdefs.h"
+
#define LOG_BUF_SIZE 1024
#if FAKE_LOG_DEVICE
@@ -56,10 +58,6 @@
static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);
static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
-
#if !defined(_WIN32)
static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -106,7 +104,7 @@
kLogUninitialized, kLogNotAvailable, kLogAvailable
} g_log_status = kLogUninitialized;
-int __android_log_dev_available(void)
+LIBLOG_ABI_PUBLIC int __android_log_dev_available()
{
if (g_log_status == kLogUninitialized) {
if (access("/dev/socket/logdw", W_OK) == 0)
@@ -485,7 +483,7 @@
[LOG_ID_KERNEL] = "kernel",
};
-const char *android_log_id_to_name(log_id_t log_id)
+LIBLOG_ABI_PUBLIC const char *android_log_id_to_name(log_id_t log_id)
{
if (log_id >= LOG_ID_MAX) {
log_id = LOG_ID_MAIN;
@@ -520,12 +518,14 @@
return write_to_log(log_id, vec, nr);
}
-int __android_log_write(int prio, const char *tag, const char *msg)
+LIBLOG_ABI_PUBLIC int __android_log_write(int prio, const char *tag,
+ const char *msg)
{
return __android_log_buf_write(LOG_ID_MAIN, prio, tag, msg);
}
-int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)
+LIBLOG_ABI_PUBLIC int __android_log_buf_write(int bufID, int prio,
+ const char *tag, const char *msg)
{
struct iovec vec[3];
char tmp_tag[32];
@@ -566,7 +566,8 @@
return write_to_log(bufID, vec, 3);
}
-int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list ap)
+LIBLOG_ABI_PUBLIC int __android_log_vprint(int prio, const char *tag,
+ const char *fmt, va_list ap)
{
char buf[LOG_BUF_SIZE];
@@ -575,7 +576,8 @@
return __android_log_write(prio, tag, buf);
}
-int __android_log_print(int prio, const char *tag, const char *fmt, ...)
+LIBLOG_ABI_PUBLIC int __android_log_print(int prio, const char *tag,
+ const char *fmt, ...)
{
va_list ap;
char buf[LOG_BUF_SIZE];
@@ -587,7 +589,9 @@
return __android_log_write(prio, tag, buf);
}
-int __android_log_buf_print(int bufID, int prio, const char *tag, const char *fmt, ...)
+LIBLOG_ABI_PUBLIC int __android_log_buf_print(int bufID, int prio,
+ const char *tag,
+ const char *fmt, ...)
{
va_list ap;
char buf[LOG_BUF_SIZE];
@@ -599,8 +603,10 @@
return __android_log_buf_write(bufID, prio, tag, buf);
}
-void __android_log_assert(const char *cond, const char *tag,
- const char *fmt, ...)
+LIBLOG_ABI_PUBLIC void __android_log_assert(
+ const char *cond,
+ const char *tag,
+ const char *fmt, ...)
{
char buf[LOG_BUF_SIZE];
@@ -625,7 +631,8 @@
/* NOTREACHED */
}
-int __android_log_bwrite(int32_t tag, const void *payload, size_t len)
+LIBLOG_ABI_PUBLIC int __android_log_bwrite(int32_t tag,
+ const void *payload, size_t len)
{
struct iovec vec[2];
@@ -637,7 +644,9 @@
return write_to_log(LOG_ID_EVENTS, vec, 2);
}
-int __android_log_security_bwrite(int32_t tag, const void *payload, size_t len)
+LIBLOG_ABI_PUBLIC int __android_log_security_bwrite(int32_t tag,
+ const void *payload,
+ size_t len)
{
struct iovec vec[2];
@@ -654,8 +663,8 @@
* for the general case where we're generating lists of stuff, but very
* handy if we just want to dump an integer into the log.
*/
-int __android_log_btwrite(int32_t tag, char type, const void *payload,
- size_t len)
+LIBLOG_ABI_PUBLIC int __android_log_btwrite(int32_t tag, char type,
+ const void *payload, size_t len)
{
struct iovec vec[3];
@@ -673,7 +682,7 @@
* Like __android_log_bwrite, but used for writing strings to the
* event log.
*/
-int __android_log_bswrite(int32_t tag, const char *payload)
+LIBLOG_ABI_PUBLIC int __android_log_bswrite(int32_t tag, const char *payload)
{
struct iovec vec[4];
char type = EVENT_TYPE_STRING;
@@ -695,7 +704,8 @@
* Like __android_log_security_bwrite, but used for writing strings to the
* security log.
*/
-int __android_log_security_bswrite(int32_t tag, const char *payload)
+LIBLOG_ABI_PUBLIC int __android_log_security_bswrite(int32_t tag,
+ const char *payload)
{
struct iovec vec[4];
char type = EVENT_TYPE_STRING;
diff --git a/liblog/logprint.c b/liblog/logprint.c
index 4ef62a1..02df8dd 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -34,12 +34,11 @@
#include <log/logprint.h>
#include <private/android_filesystem_config.h>
+#include "log_cdefs.h"
+
#define MS_PER_NSEC 1000000
#define US_PER_NSEC 1000
-/* open coded fragment, prevent circular dependencies */
-#define WEAK static
-
typedef struct FilterInfo_t {
char *mTag;
android_LogPriority mPri;
@@ -185,13 +184,15 @@
* returns 1 if this log line should be printed based on its priority
* and tag, and 0 if it should not
*/
-int android_log_shouldPrintLine (
- AndroidLogFormat *p_format, const char *tag, android_LogPriority pri)
+LIBLOG_ABI_PUBLIC int android_log_shouldPrintLine (
+ AndroidLogFormat *p_format,
+ const char *tag,
+ android_LogPriority pri)
{
return pri >= filterPriForTag(p_format, tag);
}
-AndroidLogFormat *android_log_format_new()
+LIBLOG_ABI_PUBLIC AndroidLogFormat *android_log_format_new()
{
AndroidLogFormat *p_ret;
@@ -213,7 +214,7 @@
static list_declare(convertHead);
-void android_log_format_free(AndroidLogFormat *p_format)
+LIBLOG_ABI_PUBLIC void android_log_format_free(AndroidLogFormat *p_format)
{
FilterInfo *p_info, *p_info_old;
@@ -236,7 +237,8 @@
}
}
-int android_log_setPrintFormat(AndroidLogFormat *p_format,
+LIBLOG_ABI_PUBLIC int android_log_setPrintFormat(
+ AndroidLogFormat *p_format,
AndroidLogPrintFormat format)
{
switch (format) {
@@ -277,7 +279,8 @@
/**
* Returns FORMAT_OFF on invalid string
*/
-AndroidLogPrintFormat android_log_formatFromString(const char * formatString)
+LIBLOG_ABI_PUBLIC AndroidLogPrintFormat android_log_formatFromString(
+ const char * formatString)
{
static AndroidLogPrintFormat format;
@@ -341,7 +344,8 @@
* Assumes single threaded execution
*/
-int android_log_addFilterRule(AndroidLogFormat *p_format,
+LIBLOG_ABI_PUBLIC int android_log_addFilterRule(
+ AndroidLogFormat *p_format,
const char *filterExpression)
{
size_t tagNameLength;
@@ -419,7 +423,8 @@
*
*/
-int android_log_addFilterString(AndroidLogFormat *p_format,
+LIBLOG_ABI_PUBLIC int android_log_addFilterString(
+ AndroidLogFormat *p_format,
const char *filterString)
{
char *filterStringCopy = strdup (filterString);
@@ -453,8 +458,9 @@
* Returns 0 on success and -1 on invalid wire format (entry will be
* in unspecified state)
*/
-int android_log_processLogBuffer(struct logger_entry *buf,
- AndroidLogEntry *entry)
+LIBLOG_ABI_PUBLIC int android_log_processLogBuffer(
+ struct logger_entry *buf,
+ AndroidLogEntry *entry)
{
entry->tv_sec = buf->sec;
entry->tv_nsec = buf->nsec;
@@ -734,9 +740,11 @@
* it however we choose, which means we can't really use a fixed-size buffer
* here.
*/
-int android_log_processBinaryLogBuffer(struct logger_entry *buf,
- AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf,
- int messageBufLen)
+LIBLOG_ABI_PUBLIC int android_log_processBinaryLogBuffer(
+ struct logger_entry *buf,
+ AndroidLogEntry *entry,
+ const EventTagMap *map,
+ char *messageBuf, int messageBufLen)
{
size_t inCount;
unsigned int tagIndex;
@@ -852,7 +860,7 @@
* _also_ be part of libutils/Unicode.cpp if its usefullness needs to
* propagate globally.
*/
-WEAK ssize_t utf8_character_length(const char *src, size_t len)
+LIBLOG_WEAK ssize_t utf8_character_length(const char *src, size_t len)
{
const char *cur = src;
const char first_char = *cur++;
@@ -956,7 +964,7 @@
return p - begin;
}
-char *readSeconds(char *e, struct timespec *t)
+static char *readSeconds(char *e, struct timespec *t)
{
unsigned long multiplier;
char *p;
@@ -1243,12 +1251,12 @@
* Returns NULL on malloc error
*/
-char *android_log_formatLogLine (
- AndroidLogFormat *p_format,
- char *defaultBuffer,
- size_t defaultBufferSize,
- const AndroidLogEntry *entry,
- size_t *p_outLength)
+LIBLOG_ABI_PUBLIC char *android_log_formatLogLine (
+ AndroidLogFormat *p_format,
+ char *defaultBuffer,
+ size_t defaultBufferSize,
+ const AndroidLogEntry *entry,
+ size_t *p_outLength)
{
#if !defined(_WIN32)
struct tm tmBuf;
@@ -1421,8 +1429,16 @@
* possibly causing heap corruption. To avoid this we double check and
* set the length at the maximum (size minus null byte)
*/
- prefixLen += MIN(len, sizeof(prefixBuf) - prefixLen);
- suffixLen = MIN(suffixLen, sizeof(suffixBuf));
+ prefixLen += len;
+ if (prefixLen >= sizeof(prefixBuf)) {
+ prefixLen = sizeof(prefixBuf) - 1;
+ prefixBuf[sizeof(prefixBuf) - 1] = '\0';
+ }
+ if (suffixLen >= sizeof(suffixBuf)) {
+ suffixLen = sizeof(suffixBuf) - 1;
+ suffixBuf[sizeof(suffixBuf) - 2] = '\n';
+ suffixBuf[sizeof(suffixBuf) - 1] = '\0';
+ }
/* the following code is tragically unreadable */
@@ -1526,10 +1542,10 @@
* Returns count bytes written
*/
-int android_log_printLogLine(
- AndroidLogFormat *p_format,
- int fd,
- const AndroidLogEntry *entry)
+LIBLOG_ABI_PUBLIC int android_log_printLogLine(
+ AndroidLogFormat *p_format,
+ int fd,
+ const AndroidLogEntry *entry)
{
int ret;
char defaultBuffer[512];
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 50ecd2d..2767f73 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -16,6 +16,7 @@
#include <fcntl.h>
#include <inttypes.h>
+#include <semaphore.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
@@ -531,9 +532,15 @@
}
static unsigned signaled;
-log_time signal_time;
+static log_time signal_time;
-static void caught_blocking(int /*signum*/)
+/*
+ * Strictly, we are not allowed to log messages in a signal context, but we
+ * do make an effort to keep the failure surface minimized, and this in-effect
+ * should catch any regressions in that effort. The odds of a logged message
+ * in a signal handler causing a lockup problem should be _very_ small.
+ */
+static void caught_blocking_signal(int /*signum*/)
{
unsigned long long v = 0xDEADBEEFA55A0000ULL;
@@ -583,7 +590,7 @@
}
}
-TEST(liblog, android_logger_list_read__cpu) {
+TEST(liblog, android_logger_list_read__cpu_signal) {
struct logger_list *logger_list;
unsigned long long v = 0xDEADBEEFA55A0000ULL;
@@ -606,7 +613,7 @@
memset(&signal_time, 0, sizeof(signal_time));
- signal(SIGALRM, caught_blocking);
+ signal(SIGALRM, caught_blocking_signal);
alarm(alarm_time);
signaled = 0;
@@ -651,7 +658,158 @@
alarm(0);
signal(SIGALRM, SIG_DFL);
- EXPECT_LT(1, count);
+ EXPECT_LE(1, count);
+
+ EXPECT_EQ(1, signals);
+
+ android_logger_list_close(logger_list);
+
+ unsigned long long uticks_end;
+ unsigned long long sticks_end;
+ get_ticks(&uticks_end, &sticks_end);
+
+ // Less than 1% in either user or system time, or both
+ const unsigned long long one_percent_ticks = alarm_time;
+ unsigned long long user_ticks = uticks_end - uticks_start;
+ unsigned long long system_ticks = sticks_end - sticks_start;
+ EXPECT_GT(one_percent_ticks, user_ticks);
+ EXPECT_GT(one_percent_ticks, system_ticks);
+ EXPECT_GT(one_percent_ticks, user_ticks + system_ticks);
+}
+
+/*
+ * Strictly, we are not allowed to log messages in a signal context, the
+ * correct way to handle this is to ensure the messages are constructed in
+ * a thread; the signal handler should only unblock the thread.
+ */
+static sem_t thread_trigger;
+
+static void caught_blocking_thread(int /*signum*/)
+{
+ sem_post(&thread_trigger);
+}
+
+static void *running_thread(void *) {
+ unsigned long long v = 0xDEADBEAFA55A0000ULL;
+
+ v += getpid() & 0xFFFF;
+
+ struct timespec timeout;
+ clock_gettime(CLOCK_REALTIME, &timeout);
+ timeout.tv_sec += 55;
+ sem_timedwait(&thread_trigger, &timeout);
+
+ ++signaled;
+ if ((signal_time.tv_sec == 0) && (signal_time.tv_nsec == 0)) {
+ signal_time = log_time(CLOCK_MONOTONIC);
+ signal_time.tv_sec += 2;
+ }
+
+ LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
+
+ return NULL;
+}
+
+int start_thread()
+{
+ sem_init(&thread_trigger, 0, 0);
+
+ pthread_attr_t attr;
+ if (pthread_attr_init(&attr)) {
+ return -1;
+ }
+
+ struct sched_param param;
+
+ memset(¶m, 0, sizeof(param));
+ pthread_attr_setschedparam(&attr, ¶m);
+ pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
+
+ if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
+ pthread_attr_destroy(&attr);
+ return -1;
+ }
+
+ pthread_t thread;
+ if (pthread_create(&thread, &attr, running_thread, NULL)) {
+ pthread_attr_destroy(&attr);
+ return -1;
+ }
+
+ pthread_attr_destroy(&attr);
+ return 0;
+}
+
+TEST(liblog, android_logger_list_read__cpu_thread) {
+ struct logger_list *logger_list;
+ unsigned long long v = 0xDEADBEAFA55A0000ULL;
+
+ pid_t pid = getpid();
+
+ v += pid & 0xFFFF;
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_EVENTS, ANDROID_LOG_RDONLY, 1000, pid)));
+
+ int count = 0;
+
+ int signals = 0;
+
+ unsigned long long uticks_start;
+ unsigned long long sticks_start;
+ get_ticks(&uticks_start, &sticks_start);
+
+ const unsigned alarm_time = 10;
+
+ memset(&signal_time, 0, sizeof(signal_time));
+
+ signaled = 0;
+ EXPECT_EQ(0, start_thread());
+
+ signal(SIGALRM, caught_blocking_thread);
+ alarm(alarm_time);
+
+ do {
+ log_msg log_msg;
+ if (LOG_FAILURE_RETRY(android_logger_list_read(logger_list, &log_msg)) <= 0) {
+ break;
+ }
+
+ alarm(alarm_time);
+
+ ++count;
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.len != (4 + 1 + 8))
+ || (log_msg.id() != LOG_ID_EVENTS)) {
+ continue;
+ }
+
+ char *eventData = log_msg.msg();
+
+ if (eventData[4] != EVENT_TYPE_LONG) {
+ continue;
+ }
+
+ unsigned long long l = eventData[4 + 1 + 0] & 0xFF;
+ l |= (unsigned long long) (eventData[4 + 1 + 1] & 0xFF) << 8;
+ l |= (unsigned long long) (eventData[4 + 1 + 2] & 0xFF) << 16;
+ l |= (unsigned long long) (eventData[4 + 1 + 3] & 0xFF) << 24;
+ l |= (unsigned long long) (eventData[4 + 1 + 4] & 0xFF) << 32;
+ l |= (unsigned long long) (eventData[4 + 1 + 5] & 0xFF) << 40;
+ l |= (unsigned long long) (eventData[4 + 1 + 6] & 0xFF) << 48;
+ l |= (unsigned long long) (eventData[4 + 1 + 7] & 0xFF) << 56;
+
+ if (l == v) {
+ ++signals;
+ break;
+ }
+ } while (!signaled || (log_time(CLOCK_MONOTONIC) < signal_time));
+ alarm(0);
+ signal(SIGALRM, SIG_DFL);
+
+ EXPECT_LE(1, count);
EXPECT_EQ(1, signals);
@@ -861,6 +1019,60 @@
EXPECT_LE(SIZEOF_MAX_PAYLOAD_BUF, static_cast<size_t>(max_len));
}
+TEST(liblog, __android_log_buf_print__maxtag) {
+ struct logger_list *logger_list;
+
+ pid_t pid = getpid();
+
+ ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+ LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+ log_time ts(android_log_clockid());
+
+ EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
+ max_payload_buf, max_payload_buf));
+ usleep(1000000);
+
+ int count = 0;
+
+ for (;;) {
+ log_msg log_msg;
+ if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+ break;
+ }
+
+ ASSERT_EQ(log_msg.entry.pid, pid);
+
+ if ((log_msg.entry.sec < (ts.tv_sec - 1))
+ || ((ts.tv_sec + 1) < log_msg.entry.sec)
+ || ((size_t)log_msg.entry.len < LOGGER_ENTRY_MAX_PAYLOAD)
+ || (log_msg.id() != LOG_ID_MAIN)) {
+ continue;
+ }
+
+ ++count;
+
+ AndroidLogFormat *logformat = android_log_format_new();
+ EXPECT_TRUE(NULL != logformat);
+ AndroidLogEntry entry;
+ int processLogBuffer = android_log_processLogBuffer(&log_msg.entry_v1,
+ &entry);
+ EXPECT_EQ(0, processLogBuffer);
+ if (processLogBuffer == 0) {
+ fflush(stderr);
+ int printLogLine =
+ android_log_printLogLine(logformat, fileno(stderr), &entry);
+ EXPECT_LE(128, printLogLine);
+ EXPECT_GT(LOGGER_ENTRY_MAX_PAYLOAD, printLogLine);
+ }
+ android_log_format_free(logformat);
+ }
+
+ EXPECT_EQ(1, count);
+
+ android_logger_list_close(logger_list);
+}
+
TEST(liblog, too_big_payload) {
pid_t pid = getpid();
static const char big_payload_tag[] = "TEST_big_payload_XXXX";
@@ -978,11 +1190,20 @@
struct logger * logger;
EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id)));
EXPECT_EQ(id, android_logger_get_id(logger));
- EXPECT_LT(0, android_logger_get_log_size(logger));
- /* crash buffer is allowed to be empty, that is actually healthy! */
- if (android_logger_get_log_readable_size(logger) ||
- (strcmp("crash", name) && strcmp("security", name))) {
- EXPECT_LT(0, android_logger_get_log_readable_size(logger));
+ ssize_t get_log_size = android_logger_get_log_size(logger);
+ /* security buffer is allowed to be denied */
+ if (strcmp("security", name)) {
+ EXPECT_LT(0, get_log_size);
+ /* crash buffer is allowed to be empty, that is actually healthy! */
+ EXPECT_LE((strcmp("crash", name)) != 0,
+ android_logger_get_log_readable_size(logger));
+ } else {
+ EXPECT_NE(0, get_log_size);
+ if (get_log_size < 0) {
+ EXPECT_GT(0, android_logger_get_log_readable_size(logger));
+ } else {
+ EXPECT_LE(0, android_logger_get_log_readable_size(logger));
+ }
}
EXPECT_LT(0, android_logger_get_log_version(logger));
}
diff --git a/liblog/uio.c b/liblog/uio.c
index f77cc49..d0184dc 100644
--- a/liblog/uio.c
+++ b/liblog/uio.c
@@ -16,17 +16,20 @@
#if defined(_WIN32)
-#include <log/uio.h>
#include <unistd.h>
-int readv( int fd, struct iovec* vecs, int count )
+#include <log/uio.h>
+
+#include "log_cdefs.h"
+
+LIBLOG_ABI_PUBLIC int readv(int fd, struct iovec *vecs, int count)
{
int total = 0;
for ( ; count > 0; count--, vecs++ ) {
char* buf = vecs->iov_base;
int len = vecs->iov_len;
-
+
while (len > 0) {
int ret = read( fd, buf, len );
if (ret < 0) {
@@ -46,14 +49,14 @@
return total;
}
-int writev( int fd, const struct iovec* vecs, int count )
+LIBLOG_ABI_PUBLIC int writev(int fd, const struct iovec *vecs, int count)
{
int total = 0;
for ( ; count > 0; count--, vecs++ ) {
const char* buf = vecs->iov_base;
int len = vecs->iov_len;
-
+
while (len > 0) {
int ret = write( fd, buf, len );
if (ret < 0) {
@@ -69,7 +72,7 @@
len -= ret;
}
}
-Exit:
+Exit:
return total;
}
diff --git a/libmemunreachable/Allocator.cpp b/libmemunreachable/Allocator.cpp
index b75f1e5..68f654c 100644
--- a/libmemunreachable/Allocator.cpp
+++ b/libmemunreachable/Allocator.cpp
@@ -370,11 +370,11 @@
}
void* HeapImpl::AllocLocked(size_t size) {
- if (__predict_false(size > kMaxBucketAllocationSize)) {
+ if (size > kMaxBucketAllocationSize) {
return MapAlloc(size);
}
int bucket = size_to_bucket(size);
- if (__predict_false(free_chunks_[bucket].empty())) {
+ if (free_chunks_[bucket].empty()) {
Chunk *chunk = new Chunk(this, bucket);
free_chunks_[bucket].insert(chunk->node_);
}
diff --git a/libmemunreachable/Allocator.h b/libmemunreachable/Allocator.h
index b0a4d4c..a8f579e 100644
--- a/libmemunreachable/Allocator.h
+++ b/libmemunreachable/Allocator.h
@@ -24,6 +24,7 @@
#include <map>
#include <memory>
#include <set>
+#include <unordered_map>
#include <unordered_set>
#include <vector>
extern std::atomic<int> heap_count;
@@ -209,9 +210,12 @@
template<class T>
using list = std::list<T, Allocator<T>>;
-template<class T, class Key, class Compare = std::less<Key>>
+template<class Key, class T, class Compare = std::less<Key>>
using map = std::map<Key, T, Compare, Allocator<std::pair<const Key, T>>>;
+template<class Key, class T, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
+using unordered_map = std::unordered_map<Key, T, Hash, KeyEqual, Allocator<std::pair<const Key, T>>>;
+
template<class Key, class Hash = std::hash<Key>, class KeyEqual = std::equal_to<Key>>
using unordered_set = std::unordered_set<Key, Hash, KeyEqual, Allocator<Key>>;
diff --git a/libmemunreachable/Android.mk b/libmemunreachable/Android.mk
index 0df26a8..7b66d44 100644
--- a/libmemunreachable/Android.mk
+++ b/libmemunreachable/Android.mk
@@ -3,6 +3,7 @@
memunreachable_srcs := \
Allocator.cpp \
HeapWalker.cpp \
+ LeakFolding.cpp \
LeakPipe.cpp \
LineBuffer.cpp \
MemUnreachable.cpp \
@@ -12,7 +13,9 @@
memunreachable_test_srcs := \
tests/Allocator_test.cpp \
+ tests/DisableMalloc_test.cpp \
tests/HeapWalker_test.cpp \
+ tests/LeakFolding_test.cpp \
tests/MemUnreachable_test.cpp \
tests/ThreadCapture_test.cpp \
@@ -41,3 +44,22 @@
LOCAL_SHARED_LIBRARIES := libmemunreachable libbase liblog
include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := memunreachable_test
+LOCAL_SRC_FILES := \
+ Allocator.cpp \
+ HeapWalker.cpp \
+ LeakFolding.cpp \
+ tests/Allocator_test.cpp \
+ tests/HeapWalker_test.cpp \
+ tests/HostMallocStub.cpp \
+ tests/LeakFolding_test.cpp \
+
+LOCAL_CFLAGS := -std=c++14 -Wall -Wextra -Werror
+LOCAL_CLANG := true
+LOCAL_SHARED_LIBRARIES := libbase liblog
+LOCAL_MODULE_HOST_OS := linux
+
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libmemunreachable/HeapWalker.cpp b/libmemunreachable/HeapWalker.cpp
index 1a0c33d..19393ec 100644
--- a/libmemunreachable/HeapWalker.cpp
+++ b/libmemunreachable/HeapWalker.cpp
@@ -21,17 +21,19 @@
#include "Allocator.h"
#include "HeapWalker.h"
+#include "LeakFolding.h"
#include "log.h"
bool HeapWalker::Allocation(uintptr_t begin, uintptr_t end) {
if (end == begin) {
end = begin + 1;
}
- auto inserted = allocations_.insert(std::pair<Range, RangeInfo>(Range{begin, end}, RangeInfo{false, false}));
+ Range range{begin, end};
+ auto inserted = allocations_.insert(std::pair<Range, AllocationInfo>(range, AllocationInfo{}));
if (inserted.second) {
valid_allocations_range_.begin = std::min(valid_allocations_range_.begin, begin);
valid_allocations_range_.end = std::max(valid_allocations_range_.end, end);
- allocation_bytes_ += end - begin;
+ allocation_bytes_ += range.size();
return true;
} else {
Range overlap = inserted.first->first;
@@ -44,27 +46,30 @@
}
}
-void HeapWalker::Walk(const Range& range, bool RangeInfo::*flag) {
- allocator::vector<Range> to_do(1, range, allocator_);
+bool HeapWalker::IsAllocationPtr(uintptr_t ptr, Range* range, AllocationInfo** info) {
+ if (ptr >= valid_allocations_range_.begin && ptr < valid_allocations_range_.end) {
+ AllocationMap::iterator it = allocations_.find(Range{ptr, ptr + 1});
+ if (it != allocations_.end()) {
+ *range = it->first;
+ *info = &it->second;
+ return true;
+ }
+ }
+ return false;
+}
+
+void HeapWalker::RecurseRoot(const Range& root) {
+ allocator::vector<Range> to_do(1, root, allocator_);
while (!to_do.empty()) {
Range range = to_do.back();
to_do.pop_back();
- uintptr_t begin = (range.begin + (sizeof(uintptr_t) - 1)) & ~(sizeof(uintptr_t) - 1);
- // TODO(ccross): we might need to consider a pointer to the end of a buffer
- // to be inside the buffer, which means the common case of a pointer to the
- // beginning of a buffer may keep two ranges live.
- for (uintptr_t i = begin; i < range.end; i += sizeof(uintptr_t)) {
- uintptr_t val = *reinterpret_cast<uintptr_t*>(i);
- if (val >= valid_allocations_range_.begin && val < valid_allocations_range_.end) {
- RangeMap::iterator it = allocations_.find(Range{val, val + 1});
- if (it != allocations_.end()) {
- if (!(it->second.*flag)) {
- to_do.push_back(it->first);
- it->second.*flag = true;
- }
- }
+
+ ForEachPtrInRange(range, [&](Range& ref_range, AllocationInfo* ref_info) {
+ if (!ref_info->referenced_from_root) {
+ ref_info->referenced_from_root = true;
+ to_do.push_back(ref_range);
}
- }
+ });
}
}
@@ -85,27 +90,22 @@
}
bool HeapWalker::DetectLeaks() {
+ // Recursively walk pointers from roots to mark referenced allocations
for (auto it = roots_.begin(); it != roots_.end(); it++) {
- Walk(*it, &RangeInfo::referenced_from_root);
+ RecurseRoot(*it);
}
Range vals;
vals.begin = reinterpret_cast<uintptr_t>(root_vals_.data());
vals.end = vals.begin + root_vals_.size() * sizeof(uintptr_t);
- Walk(vals, &RangeInfo::referenced_from_root);
- for (auto it = allocations_.begin(); it != allocations_.end(); it++) {
- if (!it->second.referenced_from_root) {
- Walk(it->first, &RangeInfo::referenced_from_leak);
- }
- }
+ RecurseRoot(vals);
return true;
}
bool HeapWalker::Leaked(allocator::vector<Range>& leaked, size_t limit,
size_t* num_leaks_out, size_t* leak_bytes_out) {
- DetectLeaks();
leaked.clear();
size_t num_leaks = 0;
@@ -120,7 +120,7 @@
size_t n = 0;
for (auto it = allocations_.begin(); it != allocations_.end(); it++) {
if (!it->second.referenced_from_root) {
- if (n++ <= limit) {
+ if (n++ < limit) {
leaked.push_back(it->first);
}
}
diff --git a/libmemunreachable/HeapWalker.h b/libmemunreachable/HeapWalker.h
index 4be1934..7b851c4 100644
--- a/libmemunreachable/HeapWalker.h
+++ b/libmemunreachable/HeapWalker.h
@@ -20,11 +20,14 @@
#include "android-base/macros.h"
#include "Allocator.h"
+#include "Tarjan.h"
// A range [begin, end)
struct Range {
uintptr_t begin;
uintptr_t end;
+
+ size_t size() const { return end - begin; };
};
// Comparator for Ranges that returns equivalence for overlapping ranges
@@ -34,7 +37,6 @@
}
};
-
class HeapWalker {
public:
HeapWalker(Allocator<HeapWalker> allocator) : allocator_(allocator),
@@ -55,16 +57,25 @@
size_t Allocations();
size_t AllocationBytes();
- private:
- struct RangeInfo {
+ template<class F>
+ void ForEachPtrInRange(const Range& range, F&& f);
+
+ template<class F>
+ void ForEachAllocation(F&& f);
+
+ struct AllocationInfo {
bool referenced_from_root;
- bool referenced_from_leak;
};
- void Walk(const Range& range, bool RangeInfo::* flag);
+
+ private:
+
+ void RecurseRoot(const Range& root);
+ bool IsAllocationPtr(uintptr_t ptr, Range* range, AllocationInfo** info);
+
DISALLOW_COPY_AND_ASSIGN(HeapWalker);
Allocator<HeapWalker> allocator_;
- using RangeMap = allocator::map<RangeInfo, Range, compare_range>;
- RangeMap allocations_;
+ using AllocationMap = allocator::map<Range, AllocationInfo, compare_range>;
+ AllocationMap allocations_;
size_t allocation_bytes_;
Range valid_allocations_range_;
@@ -72,4 +83,28 @@
allocator::vector<uintptr_t> root_vals_;
};
+template<class F>
+inline void HeapWalker::ForEachPtrInRange(const Range& range, F&& f) {
+ uintptr_t begin = (range.begin + (sizeof(uintptr_t) - 1)) & ~(sizeof(uintptr_t) - 1);
+ // TODO(ccross): we might need to consider a pointer to the end of a buffer
+ // to be inside the buffer, which means the common case of a pointer to the
+ // beginning of a buffer may keep two ranges live.
+ for (uintptr_t i = begin; i < range.end; i += sizeof(uintptr_t)) {
+ Range ref_range;
+ AllocationInfo* ref_info;
+ if (IsAllocationPtr(*reinterpret_cast<uintptr_t*>(i), &ref_range, &ref_info)) {
+ f(ref_range, ref_info);
+ }
+ }
+}
+
+template<class F>
+inline void HeapWalker::ForEachAllocation(F&& f) {
+ for (auto& it : allocations_) {
+ const Range& range = it.first;
+ HeapWalker::AllocationInfo& allocation = it.second;
+ f(range, allocation);
+ }
+}
+
#endif
diff --git a/libmemunreachable/Leak.h b/libmemunreachable/Leak.h
new file mode 100644
index 0000000..eaeeea7
--- /dev/null
+++ b/libmemunreachable/Leak.h
@@ -0,0 +1,57 @@
+/*
+ * 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 LIBMEMUNREACHABLE_LEAK_H_
+#define LIBMEMUNREACHABLE_LEAK_H_
+
+#include <functional>
+#include <vector>
+
+#include "memunreachable/memunreachable.h"
+
+// Custom std::hash specialization so that Leak::Backtrace can be used
+// as a key in std::unordered_map.
+namespace std {
+
+template<>
+struct hash<Leak::Backtrace> {
+ std::size_t operator()(const Leak::Backtrace& key) const {
+ std::size_t seed = 0;
+
+ hash_combine(seed, key.num_frames);
+ for (size_t i = 0; i < key.num_frames; i++) {
+ hash_combine(seed, key.frames[i]);
+ }
+
+ return seed;
+ }
+
+ private:
+ template<typename T>
+ inline void hash_combine(std::size_t& seed, const T& v) const {
+ std::hash<T> hasher;
+ seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+ }
+};
+
+} // namespace std
+
+static bool operator==(const Leak::Backtrace& lhs, const Leak::Backtrace& rhs) {
+ return (lhs.num_frames == rhs.num_frames) &&
+ memcmp(lhs.frames, rhs.frames, lhs.num_frames * sizeof(lhs.frames[0])) == 0;
+}
+
+#endif
diff --git a/libmemunreachable/LeakFolding.cpp b/libmemunreachable/LeakFolding.cpp
new file mode 100644
index 0000000..be4d20c
--- /dev/null
+++ b/libmemunreachable/LeakFolding.cpp
@@ -0,0 +1,140 @@
+/*
+ * 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 "Allocator.h"
+#include "HeapWalker.h"
+#include "LeakFolding.h"
+#include "Tarjan.h"
+#include "log.h"
+
+// Converts possibly cyclic graph of leaks to a DAG by combining
+// strongly-connected components into a object, stored in the scc pointer
+// of each node in the component.
+void LeakFolding::ComputeDAG() {
+ SCCList<LeakInfo> scc_list{allocator_};
+ Tarjan(leak_graph_, scc_list);
+
+ Allocator<SCCInfo> scc_allocator = allocator_;
+
+ for (auto& scc_nodes: scc_list) {
+ Allocator<SCCInfo>::unique_ptr leak_scc;
+ leak_scc = scc_allocator.make_unique(scc_allocator);
+
+ for (auto& node: scc_nodes) {
+ node->ptr->scc = leak_scc.get();
+ leak_scc->count++;
+ leak_scc->size += node->ptr->range.size();
+ }
+
+ leak_scc_.emplace_back(std::move(leak_scc));
+ }
+
+ for (auto& it : leak_map_) {
+ LeakInfo& leak = it.second;
+ for (auto& ref: leak.node.references_out) {
+ if (leak.scc != ref->ptr->scc) {
+ leak.scc->node.Edge(&ref->ptr->scc->node);
+ }
+ }
+ }
+}
+
+void LeakFolding::AccumulateLeaks(SCCInfo* dominator) {
+ std::function<void(SCCInfo*)> walk(std::allocator_arg, allocator_,
+ [&](SCCInfo* scc) {
+ if (scc->accumulator != dominator) {
+ scc->accumulator = dominator;
+ dominator->cuumulative_size += scc->size;
+ dominator->cuumulative_count += scc->count;
+ scc->node.Foreach([&](SCCInfo* ref) {
+ walk(ref);
+ });
+ }
+ });
+ walk(dominator);
+}
+
+bool LeakFolding::FoldLeaks() {
+ Allocator<LeakInfo> leak_allocator = allocator_;
+
+ // Find all leaked allocations insert them into leak_map_ and leak_graph_
+ heap_walker_.ForEachAllocation(
+ [&](const Range& range, HeapWalker::AllocationInfo& allocation) {
+ if (!allocation.referenced_from_root) {
+ auto it = leak_map_.emplace(std::piecewise_construct,
+ std::forward_as_tuple(range),
+ std::forward_as_tuple(range, allocator_));
+ LeakInfo& leak = it.first->second;
+ leak_graph_.push_back(&leak.node);
+ }
+ });
+
+ // Find references between leaked allocations and connect them in leak_graph_
+ for (auto& it : leak_map_) {
+ LeakInfo& leak = it.second;
+ heap_walker_.ForEachPtrInRange(leak.range,
+ [&](Range& ptr_range, HeapWalker::AllocationInfo* ptr_info) {
+ if (!ptr_info->referenced_from_root) {
+ LeakInfo* ptr_leak = &leak_map_.at(ptr_range);
+ leak.node.Edge(&ptr_leak->node);
+ }
+ });
+ }
+
+ // Convert the cyclic graph to a DAG by grouping strongly connected components
+ ComputeDAG();
+
+ // Compute dominators and cuumulative sizes
+ for (auto& scc : leak_scc_) {
+ if (scc->node.references_in.size() == 0) {
+ scc->dominator = true;
+ AccumulateLeaks(scc.get());
+ }
+ }
+
+ return true;
+}
+
+bool LeakFolding::Leaked(allocator::vector<LeakFolding::Leak>& leaked,
+ size_t* num_leaks_out, size_t* leak_bytes_out) {
+ size_t num_leaks = 0;
+ size_t leak_bytes = 0;
+ for (auto& it : leak_map_) {
+ const LeakInfo& leak = it.second;
+ num_leaks++;
+ leak_bytes += leak.range.size();
+ }
+
+ for (auto& it : leak_map_) {
+ const LeakInfo& leak = it.second;
+ if (leak.scc->dominator) {
+ leaked.emplace_back(Leak{leak.range,
+ leak.scc->cuumulative_count - 1,
+ leak.scc->cuumulative_size - leak.range.size()});
+ }
+ }
+
+ if (num_leaks_out) {
+ *num_leaks_out = num_leaks;
+ }
+ if (leak_bytes_out) {
+ *leak_bytes_out = leak_bytes;
+ }
+
+ return true;
+}
diff --git a/libmemunreachable/LeakFolding.h b/libmemunreachable/LeakFolding.h
new file mode 100644
index 0000000..732d3f2
--- /dev/null
+++ b/libmemunreachable/LeakFolding.h
@@ -0,0 +1,89 @@
+/*
+ * 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 LIBMEMUNREACHABLE_LEAK_FOLDING_H_
+#define LIBMEMUNREACHABLE_LEAK_FOLDING_H_
+
+#include "HeapWalker.h"
+
+class LeakFolding {
+ public:
+ LeakFolding(Allocator<void> allocator, HeapWalker& heap_walker)
+ : allocator_(allocator), heap_walker_(heap_walker),
+ leak_map_(allocator), leak_graph_(allocator), leak_scc_(allocator) {}
+
+ bool FoldLeaks();
+
+ struct Leak {
+ const Range range;
+ size_t referenced_count;
+ size_t referenced_size;
+ };
+
+ bool Leaked(allocator::vector<Leak>& leaked,
+ size_t* num_leaks_out, size_t* leak_bytes_out);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LeakFolding);
+ Allocator<void> allocator_;
+ HeapWalker& heap_walker_;
+
+ struct SCCInfo {
+ public:
+ Node<SCCInfo> node;
+
+ size_t count;
+ size_t size;
+
+ size_t cuumulative_count;
+ size_t cuumulative_size;
+
+ bool dominator;
+ SCCInfo* accumulator;
+
+ SCCInfo(Allocator<SCCInfo> allocator) : node(this, allocator),
+ count(0), size(0), cuumulative_count(0), cuumulative_size(0),
+ dominator(false), accumulator(nullptr) {}
+ private:
+ SCCInfo(SCCInfo&&) = delete;
+ DISALLOW_COPY_AND_ASSIGN(SCCInfo);
+ };
+
+ struct LeakInfo {
+ public:
+ Node<LeakInfo> node;
+
+ const Range range;
+
+ SCCInfo* scc;
+
+ LeakInfo(const Range& range, Allocator<LeakInfo> allocator)
+ : node(this, allocator), range(range),
+ scc(nullptr) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LeakInfo);
+ };
+
+ void ComputeDAG();
+ void AccumulateLeaks(SCCInfo* dominator);
+
+ allocator::map<Range, LeakInfo, compare_range> leak_map_;
+ Graph<LeakInfo> leak_graph_;
+ allocator::vector<Allocator<SCCInfo>::unique_ptr> leak_scc_;
+};
+
+#endif // LIBMEMUNREACHABLE_LEAK_FOLDING_H_
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index eca26eb..ac19a66 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -21,12 +21,15 @@
#include <mutex>
#include <string>
#include <sstream>
+#include <unordered_map>
#include <backtrace.h>
#include <android-base/macros.h>
#include "Allocator.h"
#include "HeapWalker.h"
+#include "Leak.h"
+#include "LeakFolding.h"
#include "LeakPipe.h"
#include "ProcessMappings.h"
#include "PtracerThread.h"
@@ -117,32 +120,84 @@
return true;
}
-bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit,
- size_t* num_leaks, size_t* leak_bytes) {
+bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks,
+ size_t limit, size_t* num_leaks, size_t* leak_bytes) {
ALOGI("sweeping process %d for unreachable memory", pid_);
leaks.clear();
- allocator::vector<Range> leaked{allocator_};
- if (!heap_walker_.Leaked(leaked, limit, num_leaks, leak_bytes)) {
+ if (!heap_walker_.DetectLeaks()) {
return false;
}
- for (auto it = leaked.begin(); it != leaked.end(); it++) {
- Leak leak{};
- leak.begin = it->begin;
- leak.size = it->end - it->begin;;
- memcpy(leak.contents, reinterpret_cast<void*>(it->begin),
- std::min(leak.size, Leak::contents_length));
- ssize_t num_backtrace_frames = malloc_backtrace(reinterpret_cast<void*>(it->begin),
- leak.backtrace_frames, leak.backtrace_length);
- if (num_backtrace_frames > 0) {
- leak.num_backtrace_frames = num_backtrace_frames;
- }
- leaks.emplace_back(leak);
- }
+
+ allocator::vector<Range> leaked1{allocator_};
+ heap_walker_.Leaked(leaked1, 0, num_leaks, leak_bytes);
ALOGI("sweeping done");
+ ALOGI("folding related leaks");
+
+ LeakFolding folding(allocator_, heap_walker_);
+ if (!folding.FoldLeaks()) {
+ return false;
+ }
+
+ allocator::vector<LeakFolding::Leak> leaked{allocator_};
+
+ if (!folding.Leaked(leaked, num_leaks, leak_bytes)) {
+ return false;
+ }
+
+ allocator::unordered_map<Leak::Backtrace, Leak*> backtrace_map{allocator_};
+
+ // Prevent reallocations of backing memory so we can store pointers into it
+ // in backtrace_map.
+ leaks.reserve(leaked.size());
+
+ for (auto& it: leaked) {
+ leaks.emplace_back();
+ Leak* leak = &leaks.back();
+
+ ssize_t num_backtrace_frames = malloc_backtrace(reinterpret_cast<void*>(it.range.begin),
+ leak->backtrace.frames, leak->backtrace.max_frames);
+ if (num_backtrace_frames > 0) {
+ leak->backtrace.num_frames = num_backtrace_frames;
+
+ auto inserted = backtrace_map.emplace(leak->backtrace, leak);
+ if (!inserted.second) {
+ // Leak with same backtrace already exists, drop this one and
+ // increment similar counts on the existing one.
+ leaks.pop_back();
+ Leak* similar_leak = inserted.first->second;
+ similar_leak->similar_count++;
+ similar_leak->similar_size += it.range.size();
+ similar_leak->similar_referenced_count += it.referenced_count;
+ similar_leak->similar_referenced_size += it.referenced_size;
+ similar_leak->total_size += it.range.size();
+ similar_leak->total_size += it.referenced_size;
+ continue;
+ }
+ }
+
+ leak->begin = it.range.begin;
+ leak->size = it.range.size();
+ leak->referenced_count = it.referenced_count;
+ leak->referenced_size = it.referenced_size;
+ leak->total_size = leak->size + leak->referenced_size;
+ memcpy(leak->contents, reinterpret_cast<void*>(it.range.begin),
+ std::min(leak->size, Leak::contents_length));
+ }
+
+ ALOGI("folding done");
+
+ std::sort(leaks.begin(), leaks.end(), [](const Leak& a, const Leak& b) {
+ return a.total_size > b.total_size;
+ });
+
+ if (leaks.size() > limit) {
+ leaks.resize(limit);
+ }
+
return true;
}
@@ -203,6 +258,11 @@
return true;
}
+template<typename T>
+static inline const char* plural(T val) {
+ return (val == 1) ? "" : "s";
+}
+
bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit) {
int parent_pid = getpid();
int parent_tid = gettid();
@@ -339,9 +399,8 @@
ALOGI("unreachable memory detection done");
ALOGE("%zu bytes in %zu allocation%s unreachable out of %zu bytes in %zu allocation%s",
- info.leak_bytes, info.num_leaks, info.num_leaks == 1 ? "" : "s",
- info.allocation_bytes, info.num_allocations, info.num_allocations == 1 ? "" : "s");
-
+ info.leak_bytes, info.num_leaks, plural(info.num_leaks),
+ info.allocation_bytes, info.num_allocations, plural(info.num_allocations));
return true;
}
@@ -353,6 +412,23 @@
oss << " bytes unreachable at ";
oss << std::hex << begin;
oss << std::endl;
+ if (referenced_count > 0) {
+ oss << std::dec;
+ oss << " referencing " << referenced_size << " unreachable bytes";
+ oss << " in " << referenced_count << " allocation" << plural(referenced_count);
+ oss << std::endl;
+ }
+ if (similar_count > 0) {
+ oss << std::dec;
+ oss << " and " << similar_size << " similar unreachable bytes";
+ oss << " in " << similar_count << " allocation" << plural(similar_count);
+ oss << std::endl;
+ if (similar_referenced_count > 0) {
+ oss << " referencing " << similar_referenced_size << " unreachable bytes";
+ oss << " in " << similar_referenced_count << " allocation" << plural(similar_referenced_count);
+ oss << std::endl;
+ }
+ }
if (log_contents) {
const int bytes_per_line = 16;
@@ -361,7 +437,7 @@
if (bytes == size) {
oss << " contents:" << std::endl;
} else {
- oss << " first " << bytes << " bytes of contents:" << std::endl;
+ oss << " first " << bytes << " bytes of contents:" << std::endl;
}
for (size_t i = 0; i < bytes; i += bytes_per_line) {
@@ -385,21 +461,41 @@
oss << std::endl;
}
}
- if (num_backtrace_frames > 0) {
- oss << backtrace_string(backtrace_frames, num_backtrace_frames);
+ if (backtrace.num_frames > 0) {
+ oss << backtrace_string(backtrace.frames, backtrace.num_frames);
}
return oss.str();
}
+// Figure out the abi based on defined macros.
+#if defined(__arm__)
+#define ABI_STRING "arm"
+#elif defined(__aarch64__)
+#define ABI_STRING "arm64"
+#elif defined(__mips__) && !defined(__LP64__)
+#define ABI_STRING "mips"
+#elif defined(__mips__) && defined(__LP64__)
+#define ABI_STRING "mips64"
+#elif defined(__i386__)
+#define ABI_STRING "x86"
+#elif defined(__x86_64__)
+#define ABI_STRING "x86_64"
+#else
+#error "Unsupported ABI"
+#endif
+
std::string UnreachableMemoryInfo::ToString(bool log_contents) const {
std::ostringstream oss;
oss << " " << leak_bytes << " bytes in ";
- oss << num_leaks << " unreachable allocation" << (num_leaks == 1 ? "" : "s");
+ oss << num_leaks << " unreachable allocation" << plural(num_leaks);
+ oss << std::endl;
+ oss << " ABI: '" ABI_STRING "'" << std::endl;
oss << std::endl;
for (auto it = leaks.begin(); it != leaks.end(); it++) {
oss << it->ToString(log_contents);
+ oss << std::endl;
}
return oss.str();
diff --git a/libmemunreachable/ScopedAlarm.h b/libmemunreachable/ScopedAlarm.h
index 019deea..287f479 100644
--- a/libmemunreachable/ScopedAlarm.h
+++ b/libmemunreachable/ScopedAlarm.h
@@ -18,6 +18,7 @@
#define LIBMEMUNREACHABLE_SCOPED_ALARM_H_
#include <signal.h>
+#include <sys/time.h>
#include <chrono>
#include <functional>
diff --git a/libmemunreachable/Tarjan.h b/libmemunreachable/Tarjan.h
new file mode 100644
index 0000000..d7ecdb9
--- /dev/null
+++ b/libmemunreachable/Tarjan.h
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+
+// Based on system/update_engine/payload_generator/tarjan.cc
+
+#ifndef LIBMEMUNREACHABLE_TARJAN_H_
+#define LIBMEMUNREACHABLE_TARJAN_H_
+
+#include <algorithm>
+
+#include "Allocator.h"
+
+template<class T>
+class Node {
+ public:
+ allocator::set<Node<T>*> references_in;
+ allocator::set<Node<T>*> references_out;
+ size_t index;
+ size_t lowlink;
+
+ T* ptr;
+
+ Node(T* ptr, Allocator<Node> allocator) : references_in(allocator), references_out(allocator),
+ ptr(ptr) {};
+ Node(Node&& rhs) = default;
+ void Edge(Node<T>* ref) {
+ references_out.emplace(ref);
+ ref->references_in.emplace(this);
+ }
+ template<class F>
+ void Foreach(F&& f) {
+ for (auto& node: references_out) {
+ f(node->ptr);
+ }
+ }
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Node<T>);
+};
+
+template<class T>
+using Graph = allocator::vector<Node<T>*>;
+
+template<class T>
+using SCC = allocator::vector<Node<T>*>;
+
+template<class T>
+using SCCList = allocator::vector<SCC<T>>;
+
+template<class T>
+class TarjanAlgorithm {
+ public:
+ TarjanAlgorithm(Allocator<void> allocator) : index_(0),
+ stack_(allocator), components_(allocator) {}
+
+ void Execute(Graph<T>& graph, SCCList<T>& out);
+ private:
+ static constexpr size_t UNDEFINED_INDEX = static_cast<size_t>(-1);
+ void Tarjan(Node<T>* vertex, Graph<T>& graph);
+
+ size_t index_;
+ allocator::vector<Node<T>*> stack_;
+ SCCList<T> components_;
+};
+
+template<class T>
+void TarjanAlgorithm<T>::Execute(Graph<T>& graph, SCCList<T>& out) {
+ stack_.clear();
+ components_.clear();
+ index_ = 0;
+ for (auto& it: graph) {
+ it->index = UNDEFINED_INDEX;
+ it->lowlink = UNDEFINED_INDEX;
+ }
+
+ for (auto& it: graph) {
+ if (it->index == UNDEFINED_INDEX) {
+ Tarjan(it, graph);
+ }
+ }
+ out.swap(components_);
+}
+
+template<class T>
+void TarjanAlgorithm<T>::Tarjan(Node<T>* vertex, Graph<T>& graph) {
+ assert(vertex->index == UNDEFINED_INDEX);
+ vertex->index = index_;
+ vertex->lowlink = index_;
+ index_++;
+ stack_.push_back(vertex);
+ for (auto& it: vertex->references_out) {
+ Node<T>* vertex_next = it;
+ if (vertex_next->index == UNDEFINED_INDEX) {
+ Tarjan(vertex_next, graph);
+ vertex->lowlink = std::min(vertex->lowlink, vertex_next->lowlink);
+ } else if (std::find(stack_.begin(), stack_.end(), vertex_next) != stack_.end()) {
+ vertex->lowlink = std::min(vertex->lowlink, vertex_next->index);
+ }
+ }
+ if (vertex->lowlink == vertex->index) {
+ SCC<T> component{components_.get_allocator()};
+ Node<T>* other_vertex;
+ do {
+ other_vertex = stack_.back();
+ stack_.pop_back();
+ component.push_back(other_vertex);
+ } while (other_vertex != vertex && !stack_.empty());
+
+ components_.emplace_back(component);
+ }
+}
+
+template<class T>
+void Tarjan(Graph<T>& graph, SCCList<T>& out) {
+ TarjanAlgorithm<T> tarjan{graph.get_allocator()};
+ tarjan.Execute(graph, out);
+}
+
+#endif // LIBMEMUNREACHABLE_TARJAN_H_
diff --git a/libmemunreachable/ThreadCapture.cpp b/libmemunreachable/ThreadCapture.cpp
index 6357840..e8a8392 100644
--- a/libmemunreachable/ThreadCapture.cpp
+++ b/libmemunreachable/ThreadCapture.cpp
@@ -86,7 +86,7 @@
void PtraceDetach(pid_t tid, unsigned int signal);
bool PtraceThreadInfo(pid_t tid, ThreadInfo& thread_info);
- allocator::map<unsigned int, pid_t> captured_threads_;
+ allocator::map<pid_t, unsigned int> captured_threads_;
Allocator<ThreadCaptureImpl> allocator_;
pid_t pid_;
std::function<void(pid_t)> inject_test_func_;
diff --git a/libmemunreachable/bionic.h b/libmemunreachable/bionic.h
index 92de24a..83d07a8 100644
--- a/libmemunreachable/bionic.h
+++ b/libmemunreachable/bionic.h
@@ -18,6 +18,8 @@
#define LIBMEMUNREACHABLE_BIONIC_H_
#include <sys/cdefs.h>
+#include <stdint.h>
+#include <stdlib.h>
__BEGIN_DECLS
diff --git a/libmemunreachable/include/memunreachable/memunreachable.h b/libmemunreachable/include/memunreachable/memunreachable.h
index f4f01ce..9b227fd 100644
--- a/libmemunreachable/include/memunreachable/memunreachable.h
+++ b/libmemunreachable/include/memunreachable/memunreachable.h
@@ -27,11 +27,26 @@
struct Leak {
uintptr_t begin;
size_t size;
- size_t num_backtrace_frames;
+
+ size_t referenced_count;
+ size_t referenced_size;
+
+ size_t similar_count;
+ size_t similar_size;
+ size_t similar_referenced_count;
+ size_t similar_referenced_size;
+
+ size_t total_size;
+
static const size_t contents_length = 32;
char contents[contents_length];
- static const size_t backtrace_length = 16;
- uintptr_t backtrace_frames[backtrace_length];
+
+ struct Backtrace {
+ size_t num_frames;
+
+ static const size_t max_frames = 16;
+ uintptr_t frames[max_frames];
+ } backtrace;
std::string ToString(bool log_contents) const;
};
diff --git a/libmemunreachable/tests/Allocator_test.cpp b/libmemunreachable/tests/Allocator_test.cpp
index d8e473e..fa76ae0 100644
--- a/libmemunreachable/tests/Allocator_test.cpp
+++ b/libmemunreachable/tests/Allocator_test.cpp
@@ -15,12 +15,6 @@
*/
#include <Allocator.h>
-#include <sys/time.h>
-
-#include <chrono>
-#include <functional>
-#include <list>
-#include <vector>
#include <gtest/gtest.h>
#include <ScopedDisableMalloc.h>
@@ -28,8 +22,6 @@
std::function<void()> ScopedAlarm::func_;
-using namespace std::chrono_literals;
-
class AllocatorTest : public testing::Test {
protected:
AllocatorTest() : heap(), disable_malloc_() {}
@@ -180,94 +172,3 @@
ASSERT_NE(ptr, nullptr);
}
-
-class DisableMallocTest : public ::testing::Test {
- protected:
- void alarm(std::chrono::microseconds us) {
- std::chrono::seconds s = std::chrono::duration_cast<std::chrono::seconds>(us);
- itimerval t = itimerval();
- t.it_value.tv_sec = s.count();
- t.it_value.tv_usec = (us - s).count();
- setitimer(ITIMER_REAL, &t, NULL);
- }
-};
-
-TEST_F(DisableMallocTest, reenable) {
- ASSERT_EXIT({
- alarm(100ms);
- void *ptr1 = malloc(128);
- ASSERT_NE(ptr1, nullptr);
- free(ptr1);
- {
- ScopedDisableMalloc disable_malloc;
- }
- void *ptr2 = malloc(128);
- ASSERT_NE(ptr2, nullptr);
- free(ptr2);
- _exit(1);
- }, ::testing::ExitedWithCode(1), "");
-}
-
-TEST_F(DisableMallocTest, deadlock_allocate) {
- ASSERT_DEATH({
- void *ptr = malloc(128);
- ASSERT_NE(ptr, nullptr);
- free(ptr);
- {
- alarm(100ms);
- ScopedDisableMalloc disable_malloc;
- void* ptr = malloc(128);
- ASSERT_NE(ptr, nullptr);
- free(ptr);
- }
- }, "");
-}
-
-TEST_F(DisableMallocTest, deadlock_new) {
- ASSERT_DEATH({
- char* ptr = new(char);
- ASSERT_NE(ptr, nullptr);
- delete(ptr);
- {
- alarm(100ms);
- ScopedDisableMalloc disable_malloc;
- char* ptr = new(char);
- ASSERT_NE(ptr, nullptr);
- delete(ptr);
- }
- }, "");
-}
-
-TEST_F(DisableMallocTest, deadlock_delete) {
- ASSERT_DEATH({
- char* ptr = new(char);
- ASSERT_NE(ptr, nullptr);
- {
- alarm(250ms);
- ScopedDisableMalloc disable_malloc;
- delete(ptr);
- }
- }, "");
-}
-
-TEST_F(DisableMallocTest, deadlock_free) {
- ASSERT_DEATH({
- void *ptr = malloc(128);
- ASSERT_NE(ptr, nullptr);
- {
- alarm(100ms);
- ScopedDisableMalloc disable_malloc;
- free(ptr);
- }
- }, "");
-}
-
-TEST_F(DisableMallocTest, deadlock_fork) {
- ASSERT_DEATH({
- {
- alarm(100ms);
- ScopedDisableMalloc disable_malloc;
- fork();
- }
- }, "");
-}
diff --git a/libmemunreachable/tests/DisableMalloc_test.cpp b/libmemunreachable/tests/DisableMalloc_test.cpp
new file mode 100644
index 0000000..ea5c22c
--- /dev/null
+++ b/libmemunreachable/tests/DisableMalloc_test.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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 <sys/time.h>
+
+#include <chrono>
+#include <functional>
+
+#include <gtest/gtest.h>
+#include <ScopedDisableMalloc.h>
+
+using namespace std::chrono_literals;
+
+class DisableMallocTest : public ::testing::Test {
+ protected:
+ void alarm(std::chrono::microseconds us) {
+ std::chrono::seconds s = std::chrono::duration_cast<std::chrono::seconds>(us);
+ itimerval t = itimerval();
+ t.it_value.tv_sec = s.count();
+ t.it_value.tv_usec = (us - s).count();
+ setitimer(ITIMER_REAL, &t, NULL);
+ }
+};
+
+TEST_F(DisableMallocTest, reenable) {
+ ASSERT_EXIT({
+ alarm(100ms);
+ void *ptr1 = malloc(128);
+ ASSERT_NE(ptr1, nullptr);
+ free(ptr1);
+ {
+ ScopedDisableMalloc disable_malloc;
+ }
+ void *ptr2 = malloc(128);
+ ASSERT_NE(ptr2, nullptr);
+ free(ptr2);
+ _exit(1);
+ }, ::testing::ExitedWithCode(1), "");
+}
+
+TEST_F(DisableMallocTest, deadlock_allocate) {
+ ASSERT_DEATH({
+ void *ptr = malloc(128);
+ ASSERT_NE(ptr, nullptr);
+ free(ptr);
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ void* ptr = malloc(128);
+ ASSERT_NE(ptr, nullptr);
+ free(ptr);
+ }
+ }, "");
+}
+
+TEST_F(DisableMallocTest, deadlock_new) {
+ ASSERT_DEATH({
+ char* ptr = new(char);
+ ASSERT_NE(ptr, nullptr);
+ delete(ptr);
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ char* ptr = new(char);
+ ASSERT_NE(ptr, nullptr);
+ delete(ptr);
+ }
+ }, "");
+}
+
+TEST_F(DisableMallocTest, deadlock_delete) {
+ ASSERT_DEATH({
+ char* ptr = new(char);
+ ASSERT_NE(ptr, nullptr);
+ {
+ alarm(250ms);
+ ScopedDisableMalloc disable_malloc;
+ delete(ptr);
+ }
+ }, "");
+}
+
+TEST_F(DisableMallocTest, deadlock_free) {
+ ASSERT_DEATH({
+ void *ptr = malloc(128);
+ ASSERT_NE(ptr, nullptr);
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ free(ptr);
+ }
+ }, "");
+}
+
+TEST_F(DisableMallocTest, deadlock_fork) {
+ ASSERT_DEATH({
+ {
+ alarm(100ms);
+ ScopedDisableMalloc disable_malloc;
+ fork();
+ }
+ }, "");
+}
diff --git a/libmemunreachable/tests/HeapWalker_test.cpp b/libmemunreachable/tests/HeapWalker_test.cpp
index 9921eb6..c3e1c4d 100644
--- a/libmemunreachable/tests/HeapWalker_test.cpp
+++ b/libmemunreachable/tests/HeapWalker_test.cpp
@@ -80,6 +80,8 @@
HeapWalker heap_walker(heap_);
heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
+ ASSERT_EQ(true, heap_walker.DetectLeaks());
+
allocator::vector<Range> leaked(heap_);
size_t num_leaks = 0;
size_t leaked_bytes = 0;
@@ -106,9 +108,11 @@
heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
heap_walker.Root(buffer_begin(buffer1), buffer_end(buffer1));
+ ASSERT_EQ(true, heap_walker.DetectLeaks());
+
allocator::vector<Range> leaked(heap_);
- size_t num_leaks = SIZE_T_MAX;
- size_t leaked_bytes = SIZE_T_MAX;
+ size_t num_leaks = SIZE_MAX;
+ size_t leaked_bytes = SIZE_MAX;
ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
EXPECT_EQ(0U, num_leaks);
@@ -132,9 +136,11 @@
heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
heap_walker.Root(buffer_begin(buffer1) + i, buffer_end(buffer1) - j);
+ ASSERT_EQ(true, heap_walker.DetectLeaks());
+
allocator::vector<Range> leaked(heap_);
- size_t num_leaks = SIZE_T_MAX;
- size_t leaked_bytes = SIZE_T_MAX;
+ size_t num_leaks = SIZE_MAX;
+ size_t leaked_bytes = SIZE_MAX;
ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
EXPECT_EQ(0U, num_leaks);
@@ -143,3 +149,26 @@
}
}
}
+
+TEST_F(HeapWalkerTest, cycle) {
+ void* buffer1;
+ void* buffer2;
+
+ buffer1 = &buffer2;
+ buffer2 = &buffer1;
+
+ HeapWalker heap_walker(heap_);
+ heap_walker.Allocation(buffer_begin(buffer1), buffer_end(buffer1));
+ heap_walker.Allocation(buffer_begin(buffer2), buffer_end(buffer2));
+
+ ASSERT_EQ(true, heap_walker.DetectLeaks());
+
+ allocator::vector<Range> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, heap_walker.Leaked(leaked, 100, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(2U, num_leaks);
+ EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(2U, leaked.size());
+}
diff --git a/libmemunreachable/tests/HostMallocStub.cpp b/libmemunreachable/tests/HostMallocStub.cpp
new file mode 100644
index 0000000..a7e3f07
--- /dev/null
+++ b/libmemunreachable/tests/HostMallocStub.cpp
@@ -0,0 +1,23 @@
+/*
+ * 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 "bionic.h"
+
+void malloc_disable() {
+}
+
+void malloc_enable() {
+}
diff --git a/libmemunreachable/tests/LeakFolding_test.cpp b/libmemunreachable/tests/LeakFolding_test.cpp
new file mode 100644
index 0000000..879a3a0
--- /dev/null
+++ b/libmemunreachable/tests/LeakFolding_test.cpp
@@ -0,0 +1,427 @@
+/*
+ * 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 "HeapWalker.h"
+#include "LeakFolding.h"
+
+#include <gtest/gtest.h>
+#include <ScopedDisableMalloc.h>
+#include "Allocator.h"
+
+class LeakFoldingTest : public ::testing::Test {
+ public:
+ LeakFoldingTest() : disable_malloc_(), heap_() {}
+
+ void TearDown() {
+ ASSERT_TRUE(heap_.empty());
+ if (!HasFailure()) {
+ ASSERT_FALSE(disable_malloc_.timed_out());
+ }
+ }
+
+ protected:
+ ScopedDisableMallocTimeout disable_malloc_;
+ Heap heap_;
+};
+
+#define buffer_begin(buffer) reinterpret_cast<uintptr_t>(&buffer[0])
+#define buffer_end(buffer) (reinterpret_cast<uintptr_t>(&buffer[0]) + sizeof(buffer))
+#define ALLOCATION(heap_walker, buffer) \
+ ASSERT_EQ(true, heap_walker.Allocation(buffer_begin(buffer), buffer_end(buffer)))
+
+TEST_F(LeakFoldingTest, one) {
+ void* buffer1[1] = {nullptr};
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(1U, num_leaks);
+ EXPECT_EQ(sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(1U, leaked.size());
+ EXPECT_EQ(0U, leaked[0].referenced_count);
+ EXPECT_EQ(0U, leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, two) {
+ void* buffer1[1] = {nullptr};
+ void* buffer2[1] = {nullptr};
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(2U, num_leaks);
+ EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(2U, leaked.size());
+ EXPECT_EQ(0U, leaked[0].referenced_count);
+ EXPECT_EQ(0U, leaked[0].referenced_size);
+ EXPECT_EQ(0U, leaked[1].referenced_count);
+ EXPECT_EQ(0U, leaked[1].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, dominator) {
+ void* buffer1[1];
+ void* buffer2[1] = {nullptr};
+
+ buffer1[0] = buffer2;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(2U, num_leaks);
+ EXPECT_EQ(2*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(1U, leaked.size());
+ EXPECT_EQ(1U, leaked[0].referenced_count);
+ EXPECT_EQ(sizeof(uintptr_t), leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, cycle) {
+ void* buffer1[1];
+ void* buffer2[1];
+ void* buffer3[1];
+
+ buffer1[0] = buffer2;
+ buffer2[0] = buffer3;
+ buffer3[0] = buffer2;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+ ALLOCATION(heap_walker, buffer3);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(3U, num_leaks);
+ EXPECT_EQ(3*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(1U, leaked.size());
+ EXPECT_EQ(2U, leaked[0].referenced_count);
+ EXPECT_EQ(2*sizeof(uintptr_t), leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, dominator_cycle) {
+ void* buffer1[2] = {nullptr, nullptr};
+ void* buffer2[2];
+ void* buffer3[1] = {nullptr};
+
+ buffer1[0] = &buffer2;
+ buffer2[0] = &buffer1;
+ buffer2[1] = &buffer3;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+ ALLOCATION(heap_walker, buffer3);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(3U, num_leaks);
+ EXPECT_EQ(5*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(2U, leaked.size());
+
+ EXPECT_EQ(2U, leaked[0].referenced_count);
+ EXPECT_EQ(3*sizeof(uintptr_t), leaked[0].referenced_size);
+ EXPECT_EQ(2U, leaked[1].referenced_count);
+ EXPECT_EQ(3*sizeof(uintptr_t), leaked[1].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, two_cycles) {
+ void* buffer1[1];
+ void* buffer2[1];
+ void* buffer3[1];
+ void* buffer4[1];
+ void* buffer5[1];
+ void* buffer6[1];
+
+ buffer1[0] = buffer3;
+ buffer2[0] = buffer5;
+ buffer3[0] = buffer4;
+ buffer4[0] = buffer3;
+ buffer5[0] = buffer6;
+ buffer6[0] = buffer5;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+ ALLOCATION(heap_walker, buffer3);
+ ALLOCATION(heap_walker, buffer4);
+ ALLOCATION(heap_walker, buffer5);
+ ALLOCATION(heap_walker, buffer6);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(6U, num_leaks);
+ EXPECT_EQ(6*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(2U, leaked.size());
+ EXPECT_EQ(2U, leaked[0].referenced_count);
+ EXPECT_EQ(2*sizeof(uintptr_t), leaked[0].referenced_size);
+ EXPECT_EQ(2U, leaked[1].referenced_count);
+ EXPECT_EQ(2*sizeof(uintptr_t), leaked[1].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, two_dominator_cycles) {
+ void* buffer1[1];
+ void* buffer2[1];
+ void* buffer3[1];
+ void* buffer4[1];
+
+ buffer1[0] = buffer2;
+ buffer2[0] = buffer1;
+ buffer3[0] = buffer4;
+ buffer4[0] = buffer3;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+ ALLOCATION(heap_walker, buffer3);
+ ALLOCATION(heap_walker, buffer4);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(4U, num_leaks);
+ EXPECT_EQ(4*sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(4U, leaked.size());
+ EXPECT_EQ(1U, leaked[0].referenced_count);
+ EXPECT_EQ(sizeof(uintptr_t), leaked[0].referenced_size);
+ EXPECT_EQ(1U, leaked[1].referenced_count);
+ EXPECT_EQ(sizeof(uintptr_t), leaked[1].referenced_size);
+ EXPECT_EQ(1U, leaked[2].referenced_count);
+ EXPECT_EQ(sizeof(uintptr_t), leaked[2].referenced_size);
+ EXPECT_EQ(1U, leaked[3].referenced_count);
+ EXPECT_EQ(sizeof(uintptr_t), leaked[3].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, giant_dominator_cycle) {
+ const size_t n = 1000;
+ void* buffer[n];
+
+ HeapWalker heap_walker(heap_);
+
+ for (size_t i = 0; i < n; i ++) {
+ ASSERT_TRUE(heap_walker.Allocation(reinterpret_cast<uintptr_t>(&buffer[i]),
+ reinterpret_cast<uintptr_t>(&buffer[i+1])));
+ }
+
+ for (size_t i = 0; i < n - 1; i++) {
+ buffer[i] = &buffer[i+1];
+ }
+ buffer[n - 1] = &buffer[0];
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(n, num_leaks);
+ EXPECT_EQ(n * sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(1000U, leaked.size());
+ EXPECT_EQ(n - 1, leaked[0].referenced_count);
+ EXPECT_EQ((n - 1) * sizeof(uintptr_t), leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, giant_cycle) {
+ const size_t n = 1000;
+ void* buffer[n];
+ void* buffer1[1];
+
+ HeapWalker heap_walker(heap_);
+
+ for (size_t i = 0; i < n - 1; i++) {
+ buffer[i] = &buffer[i+1];
+ }
+ buffer[n - 1] = &buffer[0];
+
+ buffer1[0] = &buffer[0];
+
+ for (size_t i = 0; i < n; i ++) {
+ ASSERT_TRUE(heap_walker.Allocation(reinterpret_cast<uintptr_t>(&buffer[i]),
+ reinterpret_cast<uintptr_t>(&buffer[i+1])));
+ }
+
+ ALLOCATION(heap_walker, buffer1);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(n + 1, num_leaks);
+ EXPECT_EQ((n + 1) * sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(1U, leaked.size());
+ EXPECT_EQ(n, leaked[0].referenced_count);
+ EXPECT_EQ(n * sizeof(uintptr_t), leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, multipath) {
+ void* buffer1[2];
+ void* buffer2[1];
+ void* buffer3[1];
+ void* buffer4[1] = {nullptr};
+
+ // 1
+ // / \
+ // v v
+ // 2 3
+ // \ /
+ // v
+ // 4
+
+ buffer1[0] = &buffer2;
+ buffer1[1] = &buffer3;
+ buffer2[0] = &buffer4;
+ buffer3[0] = &buffer4;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+ ALLOCATION(heap_walker, buffer3);
+ ALLOCATION(heap_walker, buffer4);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(4U, num_leaks);
+ EXPECT_EQ(5 * sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(1U, leaked.size());
+ EXPECT_EQ(3U, leaked[0].referenced_count);
+ EXPECT_EQ(3 * sizeof(uintptr_t), leaked[0].referenced_size);
+}
+
+TEST_F(LeakFoldingTest, multicycle) {
+ void* buffer1[2]{};
+ void* buffer2[2]{};
+ void* buffer3[2]{};
+ void* buffer4[2]{};
+
+ // 1
+ // / ^
+ // v \
+ // 2 -> 3
+ // \ ^
+ // v /
+ // 4
+
+ buffer1[0] = &buffer2;
+ buffer2[0] = &buffer3;
+ buffer2[1] = &buffer4;
+ buffer3[0] = &buffer1;
+ buffer4[0] = &buffer3;
+
+ HeapWalker heap_walker(heap_);
+
+ ALLOCATION(heap_walker, buffer1);
+ ALLOCATION(heap_walker, buffer2);
+ ALLOCATION(heap_walker, buffer3);
+ ALLOCATION(heap_walker, buffer4);
+
+ LeakFolding folding(heap_, heap_walker);
+
+ ASSERT_TRUE(folding.FoldLeaks());
+
+ allocator::vector<LeakFolding::Leak> leaked(heap_);
+ size_t num_leaks = 0;
+ size_t leaked_bytes = 0;
+ ASSERT_EQ(true, folding.Leaked(leaked, &num_leaks, &leaked_bytes));
+
+ EXPECT_EQ(4U, num_leaks);
+ EXPECT_EQ(8 * sizeof(uintptr_t), leaked_bytes);
+ ASSERT_EQ(4U, leaked.size());
+ EXPECT_EQ(3U, leaked[0].referenced_count);
+ EXPECT_EQ(6 * sizeof(uintptr_t), leaked[0].referenced_size);
+ EXPECT_EQ(3U, leaked[1].referenced_count);
+ EXPECT_EQ(6 * sizeof(uintptr_t), leaked[1].referenced_size);
+ EXPECT_EQ(3U, leaked[2].referenced_count);
+ EXPECT_EQ(6 * sizeof(uintptr_t), leaked[2].referenced_size);
+ EXPECT_EQ(3U, leaked[3].referenced_count);
+ EXPECT_EQ(6 * sizeof(uintptr_t), leaked[3].referenced_size);
+}
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index 32a65ea..ecfd719 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -231,8 +231,10 @@
static const char* kRuntimeISA = "arm";
#elif defined(__aarch64__)
static const char* kRuntimeISA = "arm64";
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__)
static const char* kRuntimeISA = "mips";
+#elif defined(__mips__) && defined(__LP64__)
+static const char* kRuntimeISA = "mips64";
#elif defined(__i386__)
static const char* kRuntimeISA = "x86";
#elif defined(__x86_64__)
diff --git a/libnativebridge/tests/NeedsNativeBridge_test.cpp b/libnativebridge/tests/NeedsNativeBridge_test.cpp
index e1c0876..2067ed2 100644
--- a/libnativebridge/tests/NeedsNativeBridge_test.cpp
+++ b/libnativebridge/tests/NeedsNativeBridge_test.cpp
@@ -18,15 +18,17 @@
namespace android {
-static const char* kISAs[] = { "arm", "arm64", "mips", "x86", "x86_64", "random", "64arm", "64_x86",
- "64_x86_64", "", "reallylongstringabcd", nullptr };
+static const char* kISAs[] = { "arm", "arm64", "mips", "mips64", "x86", "x86_64", "random", "64arm",
+ "64_x86", "64_x86_64", "", "reallylongstringabcd", nullptr };
#if defined(__arm__)
static const char* kRuntimeISA = "arm";
#elif defined(__aarch64__)
static const char* kRuntimeISA = "arm64";
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__)
static const char* kRuntimeISA = "mips";
+#elif defined(__mips__) && defined(__LP64__)
+static const char* kRuntimeISA = "mips64";
#elif defined(__i386__)
static const char* kRuntimeISA = "x86";
#elif defined(__x86_64__)
diff --git a/libnativeloader/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
index b16c0e6..1bd3b8f 100644
--- a/libnativeloader/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -26,7 +26,7 @@
namespace android {
__attribute__((visibility("default")))
-void PreloadPublicNativeLibraries();
+void InitializeNativeLoader();
__attribute__((visibility("default")))
jstring CreateClassLoaderNamespace(JNIEnv* env,
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 86d9d77..899c98c 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -29,32 +29,14 @@
#include <string>
#include <mutex>
+#include "android-base/file.h"
#include "android-base/macros.h"
#include "android-base/strings.h"
namespace android {
-#ifdef __ANDROID__
-// TODO(dimitry): move this to system properties.
-static const char* kPublicNativeLibraries = "libandroid.so:"
- "libc.so:"
- "libdl.so:"
- "libEGL.so:"
- "libGLESv1_CM.so:"
- "libGLESv2.so:"
- "libGLESv3.so:"
- "libicui18n.so:"
- "libicuuc.so:"
- "libjnigraphics.so:"
- "liblog.so:"
- "libmediandk.so:"
- "libm.so:"
- "libOpenMAXAL.so:"
- "libOpenSLES.so:"
- "libRS.so:"
- "libstdc++.so:"
- "libwebviewchromium_plat_support.so:"
- "libz.so";
+#if defined(__ANDROID__)
+static constexpr const char* kPublicNativeLibrariesConfig = "/system/etc/public.libraries.txt";
class LibraryNamespaces {
public:
@@ -79,7 +61,8 @@
android_namespace_t* ns = FindNamespaceByClassLoader(env, class_loader);
- LOG_FATAL_IF(ns != nullptr, "There is already a namespace associated with this classloader");
+ LOG_ALWAYS_FATAL_IF(ns != nullptr,
+ "There is already a namespace associated with this classloader");
uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
if (is_shared) {
@@ -109,10 +92,30 @@
return it != namespaces_.end() ? it->second : nullptr;
}
- void PreloadPublicLibraries() {
+ void Initialize() {
+ // Read list of public native libraries from the config file.
+ std::string file_content;
+ LOG_ALWAYS_FATAL_IF(!base::ReadFileToString(kPublicNativeLibrariesConfig, &file_content),
+ "Error reading public native library list from \"%s\": %s",
+ kPublicNativeLibrariesConfig, strerror(errno));
+
+ std::vector<std::string> lines = base::Split(file_content, "\n");
+
+ std::vector<std::string> sonames;
+
+ for (const auto& line : lines) {
+ auto trimmed_line = base::Trim(line);
+ if (trimmed_line[0] == '#' || trimmed_line.empty()) {
+ continue;
+ }
+
+ sonames.push_back(trimmed_line);
+ }
+
+ public_libraries_ = base::Join(sonames, ':');
+
// android_init_namespaces() expects all the public libraries
// to be loaded so that they can be found by soname alone.
- std::vector<std::string> sonames = android::base::Split(kPublicNativeLibraries, ":");
for (const auto& soname : sonames) {
dlopen(soname.c_str(), RTLD_NOW | RTLD_NODELETE);
}
@@ -120,15 +123,19 @@
private:
bool InitPublicNamespace(const char* library_path) {
- // Some apps call dlopen from generated code unknown to linker in which
- // case linker uses anonymous namespace. See b/25844435 for details.
- initialized_ = android_init_namespaces(kPublicNativeLibraries, library_path);
+ // (http://b/25844435) - Some apps call dlopen from generated code (mono jited
+ // code is one example) unknown to linker in which case linker uses anonymous
+ // namespace. The second argument specifies the search path for the anonymous
+ // namespace which is the library_path of the classloader.
+ initialized_ = android_init_namespaces(public_libraries_.c_str(), library_path);
return initialized_;
}
bool initialized_;
std::vector<std::pair<jweak, android_namespace_t*>> namespaces_;
+ std::string public_libraries_;
+
DISALLOW_COPY_AND_ASSIGN(LibraryNamespaces);
};
@@ -141,10 +148,10 @@
}
#endif
-void PreloadPublicNativeLibraries() {
+void InitializeNativeLoader() {
#if defined(__ANDROID__)
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
- g_namespaces->PreloadPublicLibraries();
+ g_namespaces->Initialize();
#endif
}
diff --git a/libsync/sync_test.c b/libsync/sync_test.c
index ee9ea3c..9a5f7d8 100644
--- a/libsync/sync_test.c
+++ b/libsync/sync_test.c
@@ -92,7 +92,7 @@
for (j = 0; j < 2; j++) {
unsigned val = i + j * 3 + 1;
- sprintf(str, "test_fence%d-%d", i, j);
+ snprintf(str, sizeof(str), "test_fence%d-%d", i, j);
int fd = sw_sync_fence_create(sync_timeline_fd, str, val);
if (fd < 0) {
printf("can't create sync pt %d: %s", val, strerror(errno));
@@ -106,7 +106,7 @@
sync_data[3].thread_no = 3;
for (j = 0; j < 2; j++) {
- sprintf(str, "merged_fence%d", j);
+ snprintf(str, sizeof(str), "merged_fence%d", j);
sync_data[3].fd[j] = sync_merge(str, sync_data[0].fd[j], sync_data[1].fd[j]);
if (sync_data[3].fd[j] < 0) {
printf("can't merge sync pts %d and %d: %s\n",
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index 02907ad..ea1e4db 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -190,17 +190,22 @@
{
Mutex::Autolock _l(mMutex);
char buf[128];
- sprintf(buf, "Strong references on RefBase %p (weakref_type %p):\n", mBase, this);
+ snprintf(buf, sizeof(buf),
+ "Strong references on RefBase %p (weakref_type %p):\n",
+ mBase, this);
text.append(buf);
printRefsLocked(&text, mStrongRefs);
- sprintf(buf, "Weak references on RefBase %p (weakref_type %p):\n", mBase, this);
+ snprintf(buf, sizeof(buf),
+ "Weak references on RefBase %p (weakref_type %p):\n",
+ mBase, this);
text.append(buf);
printRefsLocked(&text, mWeakRefs);
}
{
char name[100];
- snprintf(name, 100, DEBUG_REFS_CALLSTACK_PATH "/%p.stack", this);
+ snprintf(name, sizeof(name), DEBUG_REFS_CALLSTACK_PATH "/%p.stack",
+ this);
int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644);
if (rc >= 0) {
write(rc, text.string(), text.length());
@@ -293,8 +298,8 @@
char buf[128];
while (refs) {
char inc = refs->ref >= 0 ? '+' : '-';
- sprintf(buf, "\t%c ID %p (ref %d):\n",
- inc, refs->id, refs->ref);
+ snprintf(buf, sizeof(buf), "\t%c ID %p (ref %d):\n",
+ inc, refs->id, refs->ref);
out->append(buf);
#if DEBUG_REFS_CALLSTACK_ENABLED
out->append(refs->stack.toString("\t\t"));
diff --git a/mkbootimg/bootimg.h b/mkbootimg/bootimg.h
index 5ab6195..60834fe 100644
--- a/mkbootimg/bootimg.h
+++ b/mkbootimg/bootimg.h
@@ -43,7 +43,14 @@
uint32_t tags_addr; /* physical addr for kernel tags */
uint32_t page_size; /* flash page size we assume */
- uint32_t unused[2]; /* future expansion: should be 0 */
+ uint32_t unused; /* reserved for future expansion: MUST be 0 */
+
+ /* operating system version and security patch level; for
+ * version "A.B.C" and patch level "Y-M-D":
+ * ver = A << 14 | B << 7 | C (7 bits for each of A, B, C)
+ * lvl = ((Y - 2000) & 127) << 4 | M (7 bits for Y, 4 bits for M)
+ * os_version = ver << 11 | lvl */
+ uint32_t os_version;
uint8_t name[BOOT_NAME_SIZE]; /* asciiz product name */
diff --git a/mkbootimg/mkbootimg b/mkbootimg/mkbootimg
index f95d703..7b04bcc 100755
--- a/mkbootimg/mkbootimg
+++ b/mkbootimg/mkbootimg
@@ -20,6 +20,7 @@
from struct import pack
from hashlib import sha1
import sys
+import re
def filesize(f):
if f is None:
@@ -47,7 +48,7 @@
def write_header(args):
BOOT_MAGIC = 'ANDROID!'.encode()
args.output.write(pack('8s', BOOT_MAGIC))
- args.output.write(pack('8I',
+ args.output.write(pack('10I',
filesize(args.kernel), # size in bytes
args.base + args.kernel_offset, # physical load addr
filesize(args.ramdisk), # size in bytes
@@ -55,8 +56,9 @@
filesize(args.second), # size in bytes
args.base + args.second_offset, # physical load addr
args.base + args.tags_offset, # physical addr for kernel tags
- args.pagesize)) # flash page size we assume
- args.output.write(pack('8x')) # future expansion: should be 0
+ args.pagesize, # flash page size we assume
+ 0, # future expansion: MUST be 0
+ (args.os_version << 11) | args.os_patch_level)) # os version and patch level
args.output.write(pack('16s', args.board.encode())) # asciiz product name
args.output.write(pack('512s', args.cmdline[:512].encode()))
@@ -97,6 +99,32 @@
def parse_int(x):
return int(x, 0)
+def parse_os_version(x):
+ match = re.search(r'^(\d{1,3})(?:\.(\d{1,3})(?:\.(\d{1,3}))?)?', x)
+ if match:
+ a = parse_int(match.group(1))
+ b = c = 0
+ if match.lastindex >= 2:
+ b = parse_int(match.group(2))
+ if match.lastindex == 3:
+ c = parse_int(match.group(3))
+ # 7 bits allocated for each field
+ assert a < 128
+ assert b < 128
+ assert c < 128
+ return (a << 14) | (b << 7) | c
+ return 0
+
+def parse_os_patch_level(x):
+ match = re.search(r'^(\d{4})-(\d{2})-(\d{2})', x)
+ if match:
+ y = parse_int(match.group(1)) - 2000
+ m = parse_int(match.group(2))
+ # 7 bits allocated for the year, 4 bits for the month
+ assert y >= 0 and y < 128
+ assert m > 0 and m <= 12
+ return (y << 4) | m
+ return 0
def parse_cmdline():
parser = ArgumentParser()
@@ -111,6 +139,10 @@
parser.add_argument('--ramdisk_offset', help='ramdisk offset', type=parse_int, default=0x01000000)
parser.add_argument('--second_offset', help='2nd bootloader offset', type=parse_int,
default=0x00f00000)
+ parser.add_argument('--os_version', help='operating system version', type=parse_os_version,
+ default=0)
+ parser.add_argument('--os_patch_level', help='operating system patch level',
+ type=parse_os_patch_level, default=0)
parser.add_argument('--tags_offset', help='tags offset', type=parse_int, default=0x00000100)
parser.add_argument('--board', help='board name', default='', action=ValidateStrLenAction,
maxlen=16)
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index d90f988..d53af2f 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -70,6 +70,11 @@
; mkdir -p $(dir $(TARGET_ROOT_OUT)/$(word 2,$(p))) \
; ln -sf $(word 1,$(p)) $(TARGET_ROOT_OUT)/$(word 2,$(p)))
endif
+# The A/B updater uses a top-level /postinstall directory to mount the new
+# system before reboot.
+ifeq ($(AB_OTA_UPDATER),true)
+ LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/postinstall
+endif
include $(BUILD_SYSTEM)/base_rules.mk
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
new file mode 100644
index 0000000..8db2ba8
--- /dev/null
+++ b/rootdir/etc/public.libraries.android.txt
@@ -0,0 +1,19 @@
+libandroid.so
+libc.so
+libdl.so
+libEGL.so
+libGLESv1_CM.so
+libGLESv2.so
+libGLESv3.so
+libicui18n.so
+libicuuc.so
+libjnigraphics.so
+liblog.so
+libmediandk.so
+libm.so
+libOpenMAXAL.so
+libOpenSLES.so
+libRS.so
+libstdc++.so
+libwebviewchromium_plat_support.so
+libz.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
new file mode 100644
index 0000000..673e115
--- /dev/null
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -0,0 +1,18 @@
+libandroid.so
+libc.so
+libdl.so
+libEGL.so
+libGLESv1_CM.so
+libGLESv2.so
+libGLESv3.so
+libicui18n.so
+libicuuc.so
+libjnigraphics.so
+liblog.so
+libmediandk.so
+libm.so
+libOpenMAXAL.so
+libOpenSLES.so
+libRS.so
+libstdc++.so
+libz.so
diff --git a/rootdir/init-debug.rc b/rootdir/init-debug.rc
index 435d4cb..44d34d8 100644
--- a/rootdir/init-debug.rc
+++ b/rootdir/init-debug.rc
@@ -6,3 +6,6 @@
on property:persist.mmc.cache_size=*
write /sys/block/mmcblk0/cache_size ${persist.mmc.cache_size}
+
+on early-init
+ mount debugfs debugfs /sys/kernel/debug
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 1f63fcf..aa32343 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -23,6 +23,9 @@
# Shouldn't be necessary, but sdcard won't start without it. http://b/22568628.
mkdir /mnt 0775 root system
+ # Set the security context of /postinstall if present.
+ restorecon /postinstall
+
start ueventd
on init
@@ -56,6 +59,17 @@
chown root system /sys/fs/cgroup/memory/sw/tasks
chmod 0660 /sys/fs/cgroup/memory/sw/tasks
+ # Create energy-aware scheduler tuning nodes
+ mkdir /sys/fs/cgroup/stune
+ mount cgroup none /sys/fs/cgroup/stune schedtune
+ mkdir /sys/fs/cgroup/stune/foreground
+ chown system system /sys/fs/cgroup/stune
+ chown system system /sys/fs/cgroup/stune/foreground
+ chown system system /sys/fs/cgroup/stune/tasks
+ chown system system /sys/fs/cgroup/stune/foreground/tasks
+ chmod 0664 /sys/fs/cgroup/stune/tasks
+ chmod 0664 /sys/fs/cgroup/stune/foreground/tasks
+
# Mount staging areas for devices managed by vold
# See storage config details at http://source.android.com/tech/storage/
mount tmpfs tmpfs /mnt mode=0755,uid=0,gid=1000
@@ -71,7 +85,6 @@
mkdir /mnt/expand 0771 system system
# Storage views to support runtime permissions
- mkdir /storage 0755 root root
mkdir /mnt/runtime 0700 root root
mkdir /mnt/runtime/default 0755 root root
mkdir /mnt/runtime/default/self 0755 root root
@@ -167,13 +180,16 @@
chown system system /dev/cpuset/foreground
chown system system /dev/cpuset/foreground/boost
chown system system /dev/cpuset/background
+ chown system system /dev/cpuset/system-background
chown system system /dev/cpuset/tasks
chown system system /dev/cpuset/foreground/tasks
chown system system /dev/cpuset/foreground/boost/tasks
chown system system /dev/cpuset/background/tasks
+ chown system system /dev/cpuset/system-background/tasks
chmod 0664 /dev/cpuset/foreground/tasks
chmod 0664 /dev/cpuset/foreground/boost/tasks
chmod 0664 /dev/cpuset/background/tasks
+ chmod 0664 /dev/cpuset/system-background/tasks
chmod 0664 /dev/cpuset/tasks
@@ -383,9 +399,6 @@
# symlink to bugreport storage location
symlink /data/data/com.android.shell/files/bugreports /data/bugreports
- # Separate location for storing security policy files on data
- mkdir /data/security 0711 system system
-
# Create all remaining /data root dirs so that they are made through init
# and get proper encryption policy installed
mkdir /data/backup 0700 system system
@@ -397,9 +410,6 @@
setusercryptopolicies /data/user
- # Reload policy from /data/security if present.
- setprop selinux.reload_policy 1
-
# Set SELinux security contexts on upgrade or policy update.
restorecon_recursive /data
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index ff25ac2..0ca38b9 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -5,4 +5,4 @@
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
- writepid /dev/cpuset/foreground/tasks
+ writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index 4e702a1..1646c0f 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -5,10 +5,10 @@
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
- writepid /dev/cpuset/foreground/tasks
+ writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
class main
socket zygote_secondary stream 660 root system
onrestart restart zygote
- writepid /dev/cpuset/foreground/tasks
+ writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 5497524..b477c8e 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -5,4 +5,4 @@
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
- writepid /dev/cpuset/foreground/tasks
+ writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 692af99..633a981 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -5,10 +5,10 @@
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
- writepid /dev/cpuset/foreground/tasks
+ writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
class main
socket zygote_secondary stream 660 root system
onrestart restart zygote
- writepid /dev/cpuset/foreground/tasks
+ writepid /dev/cpuset/foreground/tasks /sys/fs/cgroup/stune/foreground/tasks
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
index 45efe36..f862561 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/sdcard.c
@@ -1214,7 +1214,13 @@
}
out.fh = ptr_to_id(h);
out.open_flags = 0;
+
+#ifdef FUSE_STACKED_IO
+ out.lower_fd = h->fd;
+#else
out.padding = 0;
+#endif
+
fuse_reply(fuse, hdr->unique, &out, sizeof(out));
return NO_STATUS;
}
@@ -1378,7 +1384,13 @@
}
out.fh = ptr_to_id(h);
out.open_flags = 0;
+
+#ifdef FUSE_STACKED_IO
+ out.lower_fd = -1;
+#else
out.padding = 0;
+#endif
+
fuse_reply(fuse, hdr->unique, &out, sizeof(out));
return NO_STATUS;
}
@@ -1460,6 +1472,11 @@
out.major = FUSE_KERNEL_VERSION;
out.max_readahead = req->max_readahead;
out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
+
+#ifdef FUSE_STACKED_IO
+ out.flags |= FUSE_STACKED_IO;
+#endif
+
out.max_background = 32;
out.congestion_threshold = 32;
out.max_write = MAX_WRITE;
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index fc2898e..b1cdb60 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -38,7 +38,6 @@
nandread \
newfs_msdos \
ps \
- prlimit \
sendevent \
start \
stop \
diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c
index 5b98a01..27ea9e8 100644
--- a/toolbox/newfs_msdos.c
+++ b/toolbox/newfs_msdos.c
@@ -695,7 +695,7 @@
(u_int)tm->tm_min));
mk4(bsx->volid, x);
mklabel(bsx->label, opt_L ? opt_L : "NO NAME");
- sprintf(buf, "FAT%u", fat);
+ snprintf(buf, sizeof(buf), "FAT%u", fat);
setstr(bsx->type, buf, sizeof(bsx->type));
if (!opt_B) {
x1 += sizeof(struct bsx);
diff --git a/toolbox/prlimit.c b/toolbox/prlimit.c
deleted file mode 100644
index 8cf202a..0000000
--- a/toolbox/prlimit.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2014, The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-
-static void
-usage(const char *s)
-{
- fprintf(stderr, "usage: %s pid resource cur max\n", s);
- exit(EXIT_FAILURE);
-}
-
-int prlimit_main(int argc, char *argv[])
-{
- pid_t pid;
- struct rlimit64 rl;
- int resource;
- int rc;
-
- if (argc != 5)
- usage(*argv);
-
- if (sscanf(argv[1], "%d", &pid) != 1)
- usage(*argv);
-
- if (sscanf(argv[2], "%d", &resource) != 1)
- usage(*argv);
-
- if (sscanf(argv[3], "%llu", &rl.rlim_cur) != 1)
- usage(*argv);
-
- if (sscanf(argv[4], "%llu", &rl.rlim_max) != 1)
- usage(*argv);
-
- printf("setting resource %d of pid %d to [%llu,%llu]\n", resource, pid,
- rl.rlim_cur, rl.rlim_max);
- rc = prlimit64(pid, resource, &rl, NULL);
- if (rc < 0) {
- perror("prlimit");
- exit(EXIT_FAILURE);
- }
-
- return 0;
-}
diff --git a/toolbox/ps.c b/toolbox/ps.c
index 7e70c71..d366f3e 100644
--- a/toolbox/ps.c
+++ b/toolbox/ps.c
@@ -57,16 +57,16 @@
int prio, nice, rtprio, sched, psr;
struct passwd *pw;
- sprintf(statline, "/proc/%d", tid ? tid : pid);
+ snprintf(statline, sizeof(statline), "/proc/%d", tid ? tid : pid);
stat(statline, &stats);
if(tid) {
- sprintf(statline, "/proc/%d/task/%d/stat", pid, tid);
+ snprintf(statline, sizeof(statline), "/proc/%d/task/%d/stat", pid, tid);
cmdline[0] = 0;
snprintf(macline, sizeof(macline), "/proc/%d/task/%d/attr/current", pid, tid);
} else {
- sprintf(statline, "/proc/%d/stat", pid);
- sprintf(cmdline, "/proc/%d/cmdline", pid);
+ snprintf(statline, sizeof(statline), "/proc/%d/stat", pid);
+ snprintf(cmdline, sizeof(cmdline), "/proc/%d/cmdline", pid);
snprintf(macline, sizeof(macline), "/proc/%d/attr/current", pid);
int fd = open(cmdline, O_RDONLY);
if(fd == 0) {
@@ -149,7 +149,7 @@
pw = getpwuid(stats.st_uid);
if(pw == 0 || (display_flags & SHOW_NUMERIC_UID)) {
- sprintf(user,"%d",(int)stats.st_uid);
+ snprintf(user,sizeof(user),"%d",(int)stats.st_uid);
} else {
strcpy(user,pw->pw_name);
}
@@ -208,7 +208,7 @@
int fd, r;
char exeline[1024];
- sprintf(exeline, "/proc/%d/exe", pid);
+ snprintf(exeline, sizeof(exeline), "/proc/%d/exe", pid);
fd = open(exeline, O_RDONLY);
if(fd == 0) {
printf(" ");
@@ -243,7 +243,7 @@
DIR *d;
struct dirent *de;
- sprintf(tmp,"/proc/%d/task",pid);
+ snprintf(tmp,sizeof(tmp),"/proc/%d/task",pid);
d = opendir(tmp);
if(d == 0) return;
diff --git a/toolbox/top.c b/toolbox/top.c
index 6fda132..003f4c9 100644
--- a/toolbox/top.c
+++ b/toolbox/top.c
@@ -258,29 +258,29 @@
proc->pid = proc->tid = pid;
- sprintf(filename, "/proc/%d/stat", pid);
+ snprintf(filename, sizeof(filename), "/proc/%d/stat", pid);
read_stat(filename, proc);
- sprintf(filename, "/proc/%d/cmdline", pid);
+ snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid);
read_cmdline(filename, proc);
- sprintf(filename, "/proc/%d/status", pid);
+ snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
read_status(filename, proc);
read_policy(pid, proc);
proc->num_threads = 0;
} else {
- sprintf(filename, "/proc/%d/cmdline", pid);
+ snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid);
read_cmdline(filename, &cur_proc);
- sprintf(filename, "/proc/%d/status", pid);
+ snprintf(filename, sizeof(filename), "/proc/%d/status", pid);
read_status(filename, &cur_proc);
proc = NULL;
}
- sprintf(filename, "/proc/%d/task", pid);
+ snprintf(filename, sizeof(filename), "/proc/%d/task", pid);
task_dir = opendir(filename);
if (!task_dir) continue;
@@ -295,7 +295,7 @@
proc->pid = pid; proc->tid = tid;
- sprintf(filename, "/proc/%d/task/%d/stat", pid, tid);
+ snprintf(filename, sizeof(filename), "/proc/%d/task/%d/stat", pid, tid);
read_stat(filename, proc);
read_policy(tid, proc);
@@ -484,7 +484,7 @@
if (user && user->pw_name) {
user_str = user->pw_name;
} else {
- snprintf(user_buf, 20, "%d", proc->uid);
+ snprintf(user_buf, sizeof(user_buf), "%d", proc->uid);
user_str = user_buf;
}
if (!threads) {
diff --git a/trusty/libtrusty/tipc-test/tipc_test.c b/trusty/libtrusty/tipc-test/tipc_test.c
index 55d5ee6..1fb34c9 100644
--- a/trusty/libtrusty/tipc-test/tipc_test.c
+++ b/trusty/libtrusty/tipc-test/tipc_test.c
@@ -21,6 +21,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
+#include <sys/uio.h>
#include <trusty/tipc.h>
@@ -80,6 +81,8 @@
" ta2ta-ipc - execute TA to TA unittest\n"
" dev-uuid - print device uuid\n"
" ta-access - test ta-access flags\n"
+" writev - writev test\n"
+" readv - readv test\n"
"\n"
;
@@ -93,7 +96,7 @@
{
fprintf (stderr, usage, prog);
if (verbose)
- fprintf (stderr, usage_long);
+ fprintf (stderr, "%s", usage_long);
exit(code);
}
@@ -692,6 +695,171 @@
}
+static int writev_test(uint repeat, uint msgsz, bool var)
+{
+ uint i;
+ ssize_t rc;
+ size_t msg_len;
+ int echo_fd = -1;
+ char tx0_buf[msgsz];
+ char tx1_buf[msgsz];
+ char rx_buf [msgsz];
+ struct iovec iovs[2]= {{tx0_buf, 0}, {tx1_buf, 0}};
+
+ if (!opt_silent) {
+ printf("%s: repeat %u: msgsz %u: variable %s\n",
+ __func__, repeat, msgsz, var ? "true" : "false");
+ }
+
+ echo_fd = tipc_connect(dev_name, echo_name);
+ if (echo_fd < 0) {
+ fprintf(stderr, "Failed to connect to service\n");
+ return echo_fd;
+ }
+
+ for (i = 0; i < repeat; i++) {
+
+ msg_len = msgsz;
+ if (opt_variable && msgsz) {
+ msg_len = rand() % msgsz;
+ }
+
+ iovs[0].iov_len = msg_len / 3;
+ iovs[1].iov_len = msg_len - iovs[0].iov_len;
+
+ memset(tx0_buf, i + 1, iovs[0].iov_len);
+ memset(tx1_buf, i + 2, iovs[1].iov_len);
+ memset(rx_buf, i + 3, sizeof(rx_buf));
+
+ rc = writev(echo_fd, iovs, 2);
+ if (rc < 0) {
+ perror("writev_test: writev");
+ break;
+ }
+
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr,
+ "%s: %s: data size mismatch (%zd vs. %zd)\n",
+ __func__, "writev", (size_t)rc, msg_len);
+ break;
+ }
+
+ rc = read(echo_fd, rx_buf, sizeof(rx_buf));
+ if (rc < 0) {
+ perror("writev_test: read");
+ break;
+ }
+
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr,
+ "%s: %s: data size mismatch (%zd vs. %zd)\n",
+ __func__, "read", (size_t)rc, msg_len);
+ break;
+ }
+
+ if (memcmp(tx0_buf, rx_buf, iovs[0].iov_len)) {
+ fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
+ break;
+ }
+
+ if (memcmp(tx1_buf, rx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
+ fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
+ break;
+ }
+ }
+
+ tipc_close(echo_fd);
+
+ if (!opt_silent) {
+ printf("%s: done\n",__func__);
+ }
+
+ return 0;
+}
+
+static int readv_test(uint repeat, uint msgsz, bool var)
+{
+ uint i;
+ ssize_t rc;
+ size_t msg_len;
+ int echo_fd = -1;
+ char tx_buf [msgsz];
+ char rx0_buf[msgsz];
+ char rx1_buf[msgsz];
+ struct iovec iovs[2]= {{rx0_buf, 0}, {rx1_buf, 0}};
+
+ if (!opt_silent) {
+ printf("%s: repeat %u: msgsz %u: variable %s\n",
+ __func__, repeat, msgsz, var ? "true" : "false");
+ }
+
+ echo_fd = tipc_connect(dev_name, echo_name);
+ if (echo_fd < 0) {
+ fprintf(stderr, "Failed to connect to service\n");
+ return echo_fd;
+ }
+
+ for (i = 0; i < repeat; i++) {
+
+ msg_len = msgsz;
+ if (opt_variable && msgsz) {
+ msg_len = rand() % msgsz;
+ }
+
+ iovs[0].iov_len = msg_len / 3;
+ iovs[1].iov_len = msg_len - iovs[0].iov_len;
+
+ memset(tx_buf, i + 1, sizeof(tx_buf));
+ memset(rx0_buf, i + 2, iovs[0].iov_len);
+ memset(rx1_buf, i + 3, iovs[1].iov_len);
+
+ rc = write(echo_fd, tx_buf, msg_len);
+ if (rc < 0) {
+ perror("readv_test: write");
+ break;
+ }
+
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr,
+ "%s: %s: data size mismatch (%zd vs. %zd)\n",
+ __func__, "write", (size_t)rc, msg_len);
+ break;
+ }
+
+ rc = readv(echo_fd, iovs, 2);
+ if (rc < 0) {
+ perror("readv_test: readv");
+ break;
+ }
+
+ if ((size_t)rc != msg_len) {
+ fprintf(stderr,
+ "%s: %s: data size mismatch (%zd vs. %zd)\n",
+ __func__, "write", (size_t)rc, msg_len);
+ break;
+ }
+
+ if (memcmp(rx0_buf, tx_buf, iovs[0].iov_len)) {
+ fprintf(stderr, "%s: data mismatch: buf 0\n", __func__);
+ break;
+ }
+
+ if (memcmp(rx1_buf, tx_buf + iovs[0].iov_len, iovs[1].iov_len)) {
+ fprintf(stderr, "%s: data mismatch, buf 1\n", __func__);
+ break;
+ }
+ }
+
+ tipc_close(echo_fd);
+
+ if (!opt_silent) {
+ printf("%s: done\n",__func__);
+ }
+
+ return 0;
+}
+
+
int main(int argc, char **argv)
{
int rc = 0;
@@ -735,6 +903,10 @@
rc = dev_uuid_test();
} else if (strcmp(test_name, "ta-access") == 0) {
rc = ta_access_test();
+ } else if (strcmp(test_name, "writev") == 0) {
+ rc = writev_test(opt_repeat, opt_msgsize, opt_variable);
+ } else if (strcmp(test_name, "readv") == 0) {
+ rc = readv_test(opt_repeat, opt_msgsize, opt_variable);
} else {
fprintf(stderr, "Unrecognized test name '%s'\n", test_name);
print_usage_and_exit(argv[0], EXIT_FAILURE, true);
diff --git a/trusty/nvram/Android.mk b/trusty/nvram/Android.mk
new file mode 100644
index 0000000..18c54d5
--- /dev/null
+++ b/trusty/nvram/Android.mk
@@ -0,0 +1,30 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+# nvram.trusty is the Trusty NVRAM HAL module.
+include $(CLEAR_VARS)
+LOCAL_MODULE := nvram.trusty
+LOCAL_MODULE_RELATIVE_PATH := hw
+LOCAL_SRC_FILES := \
+ module.c \
+ trusty_nvram_implementation.cpp
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wall -Werror -Wextra -fvisibility=hidden
+LOCAL_STATIC_LIBRARIES := libnvram-hal
+LOCAL_SHARED_LIBRARIES := libtrusty libnvram-messages liblog
+include $(BUILD_SHARED_LIBRARY)
diff --git a/trusty/nvram/module.c b/trusty/nvram/module.c
new file mode 100644
index 0000000..06819c0
--- /dev/null
+++ b/trusty/nvram/module.c
@@ -0,0 +1,39 @@
+/*
+ * 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 <hardware/nvram.h>
+
+// This function is defined in trusty_nvram_implementation.cpp.
+int trusty_nvram_open(const hw_module_t* module,
+ const char* device_id,
+ hw_device_t** device_ptr);
+
+static struct hw_module_methods_t nvram_module_methods = {
+ .open = trusty_nvram_open,
+};
+
+struct nvram_module HAL_MODULE_INFO_SYM
+ __attribute__((visibility("default"))) = {
+ .common = {.tag = HARDWARE_MODULE_TAG,
+ .module_api_version = NVRAM_MODULE_API_VERSION_0_1,
+ .hal_api_version = HARDWARE_HAL_API_VERSION,
+ .id = NVRAM_HARDWARE_MODULE_ID,
+ .name = "Trusty NVRAM HAL",
+ .author = "The Android Open Source Project",
+ .methods = &nvram_module_methods,
+ .dso = 0,
+ .reserved = {}},
+};
diff --git a/trusty/nvram/trusty_nvram_implementation.cpp b/trusty/nvram/trusty_nvram_implementation.cpp
new file mode 100644
index 0000000..39496b4
--- /dev/null
+++ b/trusty/nvram/trusty_nvram_implementation.cpp
@@ -0,0 +1,152 @@
+/*
+ * 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 <errno.h>
+#include <string.h>
+
+#include <hardware/nvram.h>
+#include <trusty/tipc.h>
+
+#define LOG_TAG "TrustyNVRAM"
+#include <log/log.h>
+
+#include <nvram/hal/nvram_device_adapter.h>
+#include <nvram/messages/blob.h>
+#include <nvram/messages/nvram_messages.h>
+
+namespace {
+
+// Character device to open for Trusty IPC connections.
+const char kTrustyDeviceName[] = "/dev/trusty-ipc-dev0";
+
+// App identifier of the NVRAM app.
+const char kTrustyNvramAppId[] = "com.android.trusty.nvram";
+
+// |TrustyNvramImplementation| proxies requests to the Trusty NVRAM app. It
+// serializes the request objects, sends it to the Trusty app and finally reads
+// back the result and decodes it.
+class TrustyNvramImplementation : public nvram::NvramImplementation {
+ public:
+ ~TrustyNvramImplementation() override;
+
+ void Execute(const nvram::Request& request,
+ nvram::Response* response) override;
+
+ private:
+ // Connects the IPC channel to the Trusty app if it is not already open.
+ // Returns true if the channel is open, false on errors.
+ bool Connect();
+
+ // Dispatches a command to the trust app. Returns true if successful (note
+ // that the response may still indicate an error on the Trusty side), false if
+ // there are any I/O or encoding/decoding errors.
+ bool SendRequest(const nvram::Request& request,
+ nvram::Response* response);
+
+ // The file descriptor for the IPC connection to the Trusty app.
+ int tipc_nvram_fd_ = -1;
+
+ // Response buffer. This puts a hard size limit on the responses from the
+ // Trusty app. 4096 matches the maximum IPC message size currently supported
+ // by Trusty.
+ uint8_t response_buffer_[4096];
+};
+
+TrustyNvramImplementation::~TrustyNvramImplementation() {
+ if (tipc_nvram_fd_ != -1) {
+ tipc_close(tipc_nvram_fd_);
+ tipc_nvram_fd_ = -1;
+ }
+}
+
+void TrustyNvramImplementation::Execute(const nvram::Request& request,
+ nvram::Response* response) {
+ if (!SendRequest(request, response)) {
+ response->result = NV_RESULT_INTERNAL_ERROR;
+ }
+}
+
+bool TrustyNvramImplementation::Connect() {
+ if (tipc_nvram_fd_ != -1) {
+ return true;
+ }
+
+ int rc = tipc_connect(kTrustyDeviceName, kTrustyNvramAppId);
+ if (rc < 0) {
+ ALOGE("Failed to connect to Trusty NVRAM app: %s\n", strerror(-rc));
+ return false;
+ }
+
+ tipc_nvram_fd_ = rc;
+ return true;
+}
+
+bool TrustyNvramImplementation::SendRequest(const nvram::Request& request,
+ nvram::Response* response) {
+ if (!Connect()) {
+ return false;
+ }
+
+ nvram::Blob request_buffer;
+ if (!nvram::Encode(request, &request_buffer)) {
+ ALOGE("Failed to encode NVRAM request.\n");
+ return false;
+ }
+
+ ssize_t rc =
+ write(tipc_nvram_fd_, request_buffer.data(), request_buffer.size());
+ if (rc < 0) {
+ ALOGE("Failed to send NVRAM request: %s\n", strerror(-rc));
+ return false;
+ }
+ if (static_cast<size_t>(rc) != request_buffer.size()) {
+ ALOGE("Failed to send full request buffer: %zd\n", rc);
+ return false;
+ }
+
+ rc = read(tipc_nvram_fd_, response_buffer_, sizeof(response_buffer_));
+ if (rc < 0) {
+ ALOGE("Failed to read NVRAM response: %s\n", strerror(-rc));
+ return false;
+ }
+
+ if (static_cast<size_t>(rc) >= sizeof(response_buffer_)) {
+ ALOGE("NVRAM response exceeds response buffer size.\n");
+ return false;
+ }
+
+ if (!nvram::Decode(response_buffer_, static_cast<size_t>(rc), response)) {
+ ALOGE("Failed to decode NVRAM response.\n");
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace
+
+extern "C" int trusty_nvram_open(const hw_module_t* module,
+ const char* device_id,
+ hw_device_t** device_ptr) {
+ if (strcmp(NVRAM_HARDWARE_DEVICE_ID, device_id) != 0) {
+ return -EINVAL;
+ }
+
+ nvram::NvramDeviceAdapter* adapter =
+ new nvram::NvramDeviceAdapter(module, new TrustyNvramImplementation);
+ *device_ptr = adapter->as_device();
+ return 0;
+}