Merge changes from topic 'ext4_encryption_flag'
* changes:
fs_mgr: set ext4 encryption flag with tune2fs when needed
fs_mgr: refactor pre-mount logic into prepare_fs_for_mount()
diff --git a/base/chrono_utils.cpp b/base/chrono_utils.cpp
index 5eedf3b..b6bf701 100644
--- a/base/chrono_utils.cpp
+++ b/base/chrono_utils.cpp
@@ -33,5 +33,10 @@
#endif // __ANDROID__
}
+std::ostream& operator<<(std::ostream& os, const Timer& t) {
+ os << t.duration().count() << "ms";
+ return os;
+}
+
} // namespace base
} // namespace android
diff --git a/base/chrono_utils_test.cpp b/base/chrono_utils_test.cpp
index 057132d..da442f4 100644
--- a/base/chrono_utils_test.cpp
+++ b/base/chrono_utils_test.cpp
@@ -19,6 +19,9 @@
#include <time.h>
#include <chrono>
+#include <sstream>
+#include <string>
+#include <thread>
#include <gtest/gtest.h>
@@ -42,5 +45,36 @@
EXPECT_EQ(now, boot_seconds);
}
+template <typename T>
+void ExpectAboutEqual(T expected, T actual) {
+ auto expected_upper_bound = expected * 1.05f;
+ auto expected_lower_bound = expected * .95;
+ EXPECT_GT(expected_upper_bound, actual);
+ EXPECT_LT(expected_lower_bound, actual);
+}
+
+TEST(ChronoUtilsTest, TimerDurationIsSane) {
+ auto start = boot_clock::now();
+ Timer t;
+ std::this_thread::sleep_for(50ms);
+ auto stop = boot_clock::now();
+ auto stop_timer = t.duration();
+
+ auto expected = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start);
+ ExpectAboutEqual(expected, stop_timer);
+}
+
+TEST(ChronoUtilsTest, TimerOstream) {
+ Timer t;
+ std::this_thread::sleep_for(50ms);
+ auto stop_timer = t.duration().count();
+ std::stringstream os;
+ os << t;
+ decltype(stop_timer) stop_timer_from_stream;
+ os >> stop_timer_from_stream;
+ EXPECT_NE(0, stop_timer);
+ ExpectAboutEqual(stop_timer, stop_timer_from_stream);
+}
+
} // namespace base
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/base/include/android-base/chrono_utils.h b/base/include/android-base/chrono_utils.h
index 0086425..7679d4c 100644
--- a/base/include/android-base/chrono_utils.h
+++ b/base/include/android-base/chrono_utils.h
@@ -18,6 +18,9 @@
#define ANDROID_BASE_CHRONO_UTILS_H
#include <chrono>
+#include <sstream>
+
+using namespace std::chrono_literals;
namespace android {
namespace base {
@@ -31,6 +34,20 @@
static time_point now();
};
+class Timer {
+ public:
+ Timer() : start_(boot_clock::now()) {}
+
+ std::chrono::milliseconds duration() const {
+ return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_);
+ }
+
+ private:
+ boot_clock::time_point start_;
+};
+
+std::ostream& operator<<(std::ostream& os, const Timer& t);
+
} // namespace base
} // namespace android
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index a4c2160..344fa9a 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -85,12 +85,13 @@
fprintf(stderr, "Usage: %s [options]\n", cmd);
fprintf(stderr,
"options include:\n"
- " -h, --help Show this help\n"
- " -l, --log Log all metrics to logstorage\n"
- " -p, --print Dump the boot event records to the console\n"
- " -r, --record Record the timestamp of a named boot event\n"
- " --value Optional value to associate with the boot event\n"
- " --record_boot_reason Record the reason why the device booted\n"
+ " -h, --help Show this help\n"
+ " -l, --log Log all metrics to logstorage\n"
+ " -p, --print Dump the boot event records to the console\n"
+ " -r, --record Record the timestamp of a named boot event\n"
+ " --value Optional value to associate with the boot event\n"
+ " --record_boot_complete Record metrics related to the time for the device boot\n"
+ " --record_boot_reason Record the reason why the device booted\n"
" --record_time_since_factory_reset Record the time since the device was reset\n");
}
diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp
index 6970201..f57349b 100644
--- a/debuggerd/crasher/crasher.cpp
+++ b/debuggerd/crasher/crasher.cpp
@@ -134,6 +134,14 @@
free(buf); // GCC is smart enough to warn about this, but we're doing it deliberately.
}
+noinline void leak() {
+ while (true) {
+ void* mapping =
+ mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ static_cast<volatile char*>(mapping)[0] = 'a';
+ }
+}
+
noinline void sigsegv_non_null() {
int* a = (int *)(&do_action);
*a = 42;
@@ -160,8 +168,8 @@
fprintf(stderr, " stack-overflow recurse until the stack overflows\n");
fprintf(stderr, " nostack crash with a NULL stack pointer\n");
fprintf(stderr, "\n");
- fprintf(stderr, " heap-corruption cause a libc abort by corrupting the heap\n");
fprintf(stderr, " heap-usage cause a libc abort by abusing a heap function\n");
+ fprintf(stderr, " leak leak memory until we get OOM-killed\n");
fprintf(stderr, "\n");
fprintf(stderr, " abort call abort()\n");
fprintf(stderr, " assert call assert() without a function\n");
@@ -265,6 +273,8 @@
return pthread_join(0, nullptr);
} else if (!strcasecmp(arg, "heap-usage")) {
abuse_heap();
+ } else if (!strcasecmp(arg, "leak")) {
+ leak();
} else if (!strcasecmp(arg, "SIGSEGV-unmapped")) {
char* map = reinterpret_cast<char*>(mmap(nullptr, sizeof(int), PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0));
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index dbd4ec8..ff9b84f 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -32,6 +32,7 @@
#include <unistd.h>
#include <memory>
+#include <thread>
#include <android-base/file.h>
#include <android-base/properties.h>
@@ -89,34 +90,22 @@
FS_STAT_ENABLE_ENCRYPTION_FAILED = 0x40000,
};
-/*
- * gettime() - returns the time in seconds of the system's monotonic clock or
- * zero on error.
- */
-static time_t gettime(void)
-{
- struct timespec ts;
- int ret;
+// TODO: switch to inotify()
+bool fs_mgr_wait_for_file(const std::string& filename,
+ const std::chrono::milliseconds relative_timeout) {
+ auto start_time = std::chrono::steady_clock::now();
- ret = clock_gettime(CLOCK_MONOTONIC, &ts);
- if (ret < 0) {
- PERROR << "clock_gettime(CLOCK_MONOTONIC) failed";
- return 0;
+ while (true) {
+ if (!access(filename.c_str(), F_OK) || errno != ENOENT) {
+ return true;
+ }
+
+ std::this_thread::sleep_for(50ms);
+
+ auto now = std::chrono::steady_clock::now();
+ auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+ if (time_elapsed > relative_timeout) return false;
}
-
- return ts.tv_sec;
-}
-
-static int wait_for_file(const char *filename, int timeout)
-{
- struct stat info;
- time_t timeout_time = gettime() + timeout;
- int ret = -1;
-
- while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0))
- usleep(10000);
-
- return ret;
}
static void log_fs_stat(const char* blk_device, int fs_stat)
@@ -487,6 +476,16 @@
return rc;
}
+// Orange state means the device is unlocked, see the following link for details.
+// https://source.android.com/security/verifiedboot/verified-boot#device_state
+bool fs_mgr_is_device_unlocked() {
+ std::string verified_boot_state;
+ if (fs_mgr_get_boot_config("verifiedbootstate", &verified_boot_state)) {
+ return verified_boot_state == "orange";
+ }
+ return false;
+}
+
/*
* __mount(): wrapper around the mount() system call which also
* sets the underlying block device to read-only if the mount is read-only.
@@ -506,10 +505,11 @@
if ((info.st_mode & S_IFMT) == S_IFLNK)
unlink(target);
mkdir(target, 0755);
+ errno = 0;
ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options);
save_errno = errno;
- LINFO << __FUNCTION__ << "(source=" << source << ",target="
- << target << ",type=" << rec->fs_type << ")=" << ret;
+ PINFO << __FUNCTION__ << "(source=" << source << ",target=" << target
+ << ",type=" << rec->fs_type << ")=" << ret;
if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {
fs_mgr_set_blk_ro(source);
}
@@ -762,19 +762,6 @@
}
}
-// TODO: add ueventd notifiers if they don't exist.
-// This is just doing a wait_for_device for maximum of 1s
-int fs_mgr_test_access(const char *device) {
- int tries = 25;
- while (tries--) {
- if (!access(device, F_OK) || errno != ENOENT) {
- return 0;
- }
- usleep(40 * 1000);
- }
- return -1;
-}
-
bool is_device_secure() {
int ret = -1;
char value[PROP_VALUE_MAX];
@@ -843,8 +830,10 @@
}
}
- if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
- wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
+ if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
+ !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
+ LERROR << "Skipping '" << fstab->recs[i].blk_device << "' during mount_all";
+ continue;
}
if (fstab->recs[i].fs_mgr_flags & MF_AVB) {
@@ -1047,8 +1036,9 @@
}
/* First check the filesystem if requested */
- if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
- wait_for_file(n_blk_device, WAIT_TIMEOUT);
+ if (fstab->recs[i].fs_mgr_flags & MF_WAIT && !fs_mgr_wait_for_file(n_blk_device, 20s)) {
+ LERROR << "Skipping mounting '" << n_blk_device << "'";
+ continue;
}
int fs_stat = prepare_fs_for_mount(n_blk_device, &fstab->recs[i]);
@@ -1210,8 +1200,11 @@
fclose(zram_fp);
}
- if (fstab->recs[i].fs_mgr_flags & MF_WAIT) {
- wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);
+ if (fstab->recs[i].fs_mgr_flags & MF_WAIT &&
+ !fs_mgr_wait_for_file(fstab->recs[i].blk_device, 20s)) {
+ LERROR << "Skipping mkswap for '" << fstab->recs[i].blk_device << "'";
+ ret = -1;
+ continue;
}
/* Initialize the swap area */
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 2c99aa7..7824cfa 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -397,7 +397,7 @@
fstab_entry->blk_device = strdup(verity_blk_name.c_str());
// Makes sure we've set everything up properly.
- if (wait_for_verity_dev && fs_mgr_test_access(verity_blk_name.c_str()) < 0) {
+ if (wait_for_verity_dev && !fs_mgr_wait_for_file(verity_blk_name, 1s)) {
return false;
}
@@ -473,16 +473,6 @@
return true;
}
-// Orange state means the device is unlocked, see the following link for details.
-// https://source.android.com/security/verifiedboot/verified-boot#device_state
-static inline bool IsDeviceUnlocked() {
- std::string verified_boot_state;
- if (fs_mgr_get_boot_config("verifiedbootstate", &verified_boot_state)) {
- return verified_boot_state == "orange";
- }
- return false;
-}
-
FsManagerAvbUniquePtr FsManagerAvbHandle::Open(const fstab& fstab) {
FsManagerAvbOps avb_ops(fstab);
return DoOpen(&avb_ops);
@@ -498,7 +488,7 @@
}
FsManagerAvbUniquePtr FsManagerAvbHandle::DoOpen(FsManagerAvbOps* avb_ops) {
- bool is_device_unlocked = IsDeviceUnlocked();
+ bool is_device_unlocked = fs_mgr_is_device_unlocked();
FsManagerAvbUniquePtr avb_handle(new FsManagerAvbHandle());
if (!avb_handle) {
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
index ba1262f..43879fe 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -142,10 +142,8 @@
}
std::string path = iter->second;
- // Ensures the device path (a symlink created by init) is ready to
- // access. fs_mgr_test_access() will test a few iterations if the
- // path doesn't exist yet.
- if (fs_mgr_test_access(path.c_str()) < 0) {
+ // Ensures the device path (a symlink created by init) is ready to access.
+ if (!fs_mgr_wait_for_file(path, 1s)) {
return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
}
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index ab5beed..9117667 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include <string>
+
#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -51,9 +53,11 @@
// lastly, check the device tree
if (is_dt_compatible()) {
std::string file_name = kAndroidDtDir + "/" + key;
- // DT entries terminate with '\0' but so do the properties
if (android::base::ReadFileToString(file_name, out_val)) {
- return true;
+ if (!out_val->empty()) {
+ out_val->pop_back(); // Trims the trailing '\0' out.
+ return true;
+ }
}
}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index c985462..5ea6e98 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -17,8 +17,12 @@
#ifndef __CORE_FS_MGR_PRIV_H
#define __CORE_FS_MGR_PRIV_H
+#include <chrono>
+#include <string>
+
#include <android-base/logging.h>
-#include <fs_mgr.h>
+
+#include "fs_mgr.h"
#include "fs_mgr_priv_boot_config.h"
/* The CHECK() in logging.h will use program invocation name as the tag.
@@ -43,8 +47,6 @@
#define CRYPTO_TMPFS_OPTIONS "size=256m,mode=0771,uid=1000,gid=1000"
-#define WAIT_TIMEOUT 20
-
/* fstab has the following format:
*
* Any line starting with a # is a comment and ignored
@@ -110,9 +112,13 @@
#define DM_BUF_SIZE 4096
+using namespace std::chrono_literals;
+
int fs_mgr_set_blk_ro(const char *blockdev);
-int fs_mgr_test_access(const char *device);
+bool fs_mgr_wait_for_file(const std::string& filename,
+ const std::chrono::milliseconds relative_timeout);
bool fs_mgr_update_for_slotselect(struct fstab *fstab);
+bool fs_mgr_is_device_unlocked();
bool is_dt_compatible();
bool is_device_secure();
int load_verity_state(struct fstab_rec* fstab, int* mode);
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index 8fa9370..5de0903 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -782,8 +782,8 @@
if (fec_verity_get_metadata(f, &verity) < 0) {
PERROR << "Failed to get verity metadata '" << fstab->blk_device << "'";
// Allow verity disabled when the device is unlocked without metadata
- if ("0" == android::base::GetProperty("ro.boot.flash.locked", "")) {
- retval = FS_MGR_SETUP_VERITY_DISABLED;
+ if (fs_mgr_is_device_unlocked()) {
+ retval = FS_MGR_SETUP_VERITY_SKIPPED;
LWARNING << "Allow invalid metadata when the device is unlocked";
}
goto out;
@@ -929,7 +929,7 @@
}
// make sure we've set everything up properly
- if (wait_for_verity_dev && fs_mgr_test_access(fstab->blk_device) < 0) {
+ if (wait_for_verity_dev && !fs_mgr_wait_for_file(fstab->blk_device, 1s)) {
goto out;
}
diff --git a/include/ziparchive b/include/ziparchive
deleted file mode 120000
index d8fa424..0000000
--- a/include/ziparchive
+++ /dev/null
@@ -1 +0,0 @@
-../libziparchive/include/ziparchive
\ No newline at end of file
diff --git a/init/README.md b/init/README.md
index 72b6c6b..422fdad 100644
--- a/init/README.md
+++ b/init/README.md
@@ -260,6 +260,14 @@
> Sets the child's /proc/self/oom\_score\_adj to the specified value,
which must range from -1000 to 1000.
+`shutdown <shutdown_behavior>`
+> Set shutdown behavior of the service process. When this is not specified,
+ the service is killed during shutdown process by using SIGTERM and SIGKILL.
+ The service with shutdown_behavior of "critical" is not killed during shutdown
+ until shutdown times out. When shutdown times out, even services tagged with
+ "shutdown critical" will be killed. When the service tagged with "shutdown critical"
+ is not running when shut down starts, it will be started.
+
Triggers
--------
diff --git a/init/action.cpp b/init/action.cpp
index 6900391..4ec5f17 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -16,6 +16,7 @@
#include "action.h"
+#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
@@ -90,19 +91,18 @@
}
void Action::ExecuteCommand(const Command& command) const {
- Timer t;
+ android::base::Timer t;
int result = command.InvokeFunc();
- double duration_ms = t.duration_s() * 1000;
+ auto duration = t.duration();
// Any action longer than 50ms will be warned to user as slow operation
- if (duration_ms > 50.0 ||
- android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
+ if (duration > 50ms || android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
std::string trigger_name = BuildTriggersString();
std::string cmd_str = command.BuildCommandString();
LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
- << ":" << command.line() << ") returned " << result << " took " << duration_ms
- << "ms.";
+ << ":" << command.line() << ") returned " << result << " took "
+ << duration.count() << "ms.";
}
}
@@ -326,6 +326,13 @@
}
}
+void ActionManager::ClearQueue() {
+ // We are shutting down so don't claim the oneshot builtin actions back
+ current_executing_actions_ = {};
+ event_queue_ = {};
+ current_command_ = 0;
+}
+
bool ActionParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line, std::string* err) {
std::vector<std::string> triggers(args.begin() + 1, args.end());
diff --git a/init/action.h b/init/action.h
index 5cb50a7..ad15f3f 100644
--- a/init/action.h
+++ b/init/action.h
@@ -104,6 +104,7 @@
void ExecuteOneCommand();
bool HasMoreCommands() const;
void DumpState() const;
+ void ClearQueue();
private:
ActionManager(ActionManager const&) = delete;
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 00ffbc3..dfd7b73 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -39,6 +39,7 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
@@ -119,7 +120,7 @@
LOG(ERROR) << "failed to set bootloader message: " << err;
return -1;
}
- DoReboot(ANDROID_RB_RESTART2, "reboot", "recovery", false);
+ property_set("sys.powerctl", "reboot,recovery");
return 0;
}
@@ -538,9 +539,9 @@
}
std::string prop_name = "ro.boottime.init.mount_all."s + prop_post_fix;
- Timer t;
+ android::base::Timer t;
int ret = mount_fstab(fstabfile, mount_mode);
- property_set(prop_name, std::to_string(t.duration_ms()));
+ property_set(prop_name, std::to_string(t.duration().count()));
if (import_rc) {
/* Paths of .rc files are specified at the 2nd argument and beyond */
diff --git a/init/devices.cpp b/init/devices.cpp
index 215d2ea..13cf991 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -219,7 +219,7 @@
return {0600, 0, 0};
}
-void DeviceHandler::MakeDevice(const std::string& path, int block, int major, int minor,
+void DeviceHandler::MakeDevice(const std::string& path, bool block, int major, int minor,
const std::vector<std::string>& links) const {
auto[mode, uid, gid] = GetDevicePermissions(path, links);
mode |= (block ? S_IFBLK : S_IFCHR);
@@ -279,45 +279,6 @@
}
}
-std::vector<std::string> DeviceHandler::GetCharacterDeviceSymlinks(const Uevent& uevent) const {
- std::string parent_device;
- if (!FindPlatformDevice(uevent.path, &parent_device)) return {};
-
- // skip path to the parent driver
- std::string path = uevent.path.substr(parent_device.length());
-
- if (!StartsWith(path, "/usb")) return {};
-
- // skip root hub name and device. use device interface
- // skip 3 slashes, including the first / by starting the search at the 1st character, not 0th.
- // then extract what comes between the 3rd and 4th slash
- // e.g. "/usb/usb_device/name/tty2-1:1.0" -> "name"
-
- std::string::size_type start = 0;
- start = path.find('/', start + 1);
- if (start == std::string::npos) return {};
-
- start = path.find('/', start + 1);
- if (start == std::string::npos) return {};
-
- auto end = path.find('/', start + 1);
- if (end == std::string::npos) return {};
-
- start++; // Skip the first '/'
-
- auto length = end - start;
- if (length == 0) return {};
-
- auto name_string = path.substr(start, length);
-
- std::vector<std::string> links;
- links.emplace_back("/dev/usb/" + uevent.subsystem + name_string);
-
- mkdir("/dev/usb", 0755);
-
- return links;
-}
-
// replaces any unacceptable characters with '_', the
// length of the resulting string is equal to the input string
void SanitizePartitionName(std::string* string) {
@@ -385,7 +346,7 @@
return links;
}
-void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, int block,
+void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, bool block,
int major, int minor, const std::vector<std::string>& links) const {
if (action == "add") {
MakeDevice(devpath, block, major, minor, links);
@@ -411,31 +372,26 @@
}
}
-void DeviceHandler::HandleBlockDeviceEvent(const Uevent& uevent) const {
- // if it's not a /dev device, nothing to do
- if (uevent.major < 0 || uevent.minor < 0) return;
-
- const char* base = "/dev/block/";
- make_dir(base, 0755, sehandle_);
-
- std::string name = Basename(uevent.path);
- std::string devpath = base + name;
-
- std::vector<std::string> links;
- if (StartsWith(uevent.path, "/devices")) {
- links = GetBlockDeviceSymlinks(uevent);
+void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) {
+ if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
+ FixupSysPermissions(uevent.path, uevent.subsystem);
}
- HandleDevice(uevent.action, devpath, 1, uevent.major, uevent.minor, links);
-}
-
-void DeviceHandler::HandleGenericDeviceEvent(const Uevent& uevent) const {
// if it's not a /dev device, nothing to do
if (uevent.major < 0 || uevent.minor < 0) return;
std::string devpath;
+ std::vector<std::string> links;
+ bool block = false;
- if (StartsWith(uevent.subsystem, "usb")) {
+ if (uevent.subsystem == "block") {
+ block = true;
+ devpath = "/dev/block/" + Basename(uevent.path);
+
+ if (StartsWith(uevent.path, "/devices")) {
+ links = GetBlockDeviceSymlinks(uevent);
+ }
+ } else if (StartsWith(uevent.subsystem, "usb")) {
if (uevent.subsystem == "usb") {
if (!uevent.device_name.empty()) {
devpath = "/dev/" + uevent.device_name;
@@ -461,21 +417,7 @@
mkdir_recursive(Dirname(devpath), 0755, sehandle_);
- auto links = GetCharacterDeviceSymlinks(uevent);
-
- HandleDevice(uevent.action, devpath, 0, uevent.major, uevent.minor, links);
-}
-
-void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) {
- if (uevent.action == "add" || uevent.action == "change" || uevent.action == "online") {
- FixupSysPermissions(uevent.path, uevent.subsystem);
- }
-
- if (uevent.subsystem == "block") {
- HandleBlockDeviceEvent(uevent);
- } else {
- HandleGenericDeviceEvent(uevent);
- }
+ HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);
}
DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
diff --git a/init/devices.h b/init/devices.h
index 5105ad7..c64f5fb 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -115,16 +115,12 @@
bool FindPlatformDevice(std::string path, std::string* platform_device_path) const;
std::tuple<mode_t, uid_t, gid_t> GetDevicePermissions(
const std::string& path, const std::vector<std::string>& links) const;
- void MakeDevice(const std::string& path, int block, int major, int minor,
+ void MakeDevice(const std::string& path, bool block, int major, int minor,
const std::vector<std::string>& links) const;
- std::vector<std::string> GetCharacterDeviceSymlinks(const Uevent& uevent) const;
- void HandleDevice(const std::string& action, const std::string& devpath, int block, int major,
+ void HandleDevice(const std::string& action, const std::string& devpath, bool block, int major,
int minor, const std::vector<std::string>& links) const;
void FixupSysPermissions(const std::string& upath, const std::string& subsystem) const;
- void HandleBlockDeviceEvent(const Uevent& uevent) const;
- void HandleGenericDeviceEvent(const Uevent& uevent) const;
-
std::vector<Permissions> dev_permissions_;
std::vector<SysfsPermissions> sysfs_permissions_;
std::vector<Subsystem> subsystems_;
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
index 57d8e0f..ac4ab9b 100644
--- a/init/devices_test.cpp
+++ b/init/devices_test.cpp
@@ -30,7 +30,7 @@
class DeviceHandlerTester {
public:
void TestGetSymlinks(const std::string& platform_device, const Uevent& uevent,
- const std::vector<std::string> expected_links, bool block) {
+ const std::vector<std::string> expected_links) {
TemporaryDir fake_sys_root;
device_handler_.sysfs_mount_point_ = fake_sys_root.path;
@@ -44,11 +44,7 @@
mkdir_recursive(android::base::Dirname(fake_sys_root.path + uevent.path), 0777, nullptr);
std::vector<std::string> result;
- if (block) {
- result = device_handler_.GetBlockDeviceSymlinks(uevent);
- } else {
- result = device_handler_.GetCharacterDeviceSymlinks(uevent);
- }
+ result = device_handler_.GetBlockDeviceSymlinks(uevent);
auto expected_size = expected_links.size();
ASSERT_EQ(expected_size, result.size());
@@ -64,95 +60,6 @@
DeviceHandler device_handler_;
};
-TEST(device_handler, get_character_device_symlinks_success) {
- const char* platform_device = "/devices/platform/some_device_name";
- Uevent uevent = {
- .path = "/devices/platform/some_device_name/usb/usb_device/name/tty2-1:1.0",
- .subsystem = "tty",
- };
- std::vector<std::string> expected_result{"/dev/usb/ttyname"};
-
- DeviceHandlerTester device_handler_tester_;
- device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
-}
-
-TEST(device_handler, get_character_device_symlinks_no_pdev_match) {
- const char* platform_device = "/devices/platform/some_device_name";
- Uevent uevent = {
- .path = "/device/name/tty2-1:1.0", .subsystem = "tty",
- };
- std::vector<std::string> expected_result;
-
- DeviceHandlerTester device_handler_tester_;
- device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
-}
-
-TEST(device_handler, get_character_device_symlinks_nothing_after_platform_device) {
- const char* platform_device = "/devices/platform/some_device_name";
- Uevent uevent = {
- .path = "/devices/platform/some_device_name", .subsystem = "tty",
- };
- std::vector<std::string> expected_result;
-
- DeviceHandlerTester device_handler_tester_;
- device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
-}
-
-TEST(device_handler, get_character_device_symlinks_no_usb_found) {
- const char* platform_device = "/devices/platform/some_device_name";
- Uevent uevent = {
- .path = "/devices/platform/some_device_name/bad/bad/", .subsystem = "tty",
- };
- std::vector<std::string> expected_result;
-
- DeviceHandlerTester device_handler_tester_;
- device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
-}
-
-TEST(device_handler, get_character_device_symlinks_no_roothub) {
- const char* platform_device = "/devices/platform/some_device_name";
- Uevent uevent = {
- .path = "/devices/platform/some_device_name/usb/", .subsystem = "tty",
- };
- std::vector<std::string> expected_result;
-
- DeviceHandlerTester device_handler_tester_;
- device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
-}
-
-TEST(device_handler, get_character_device_symlinks_no_usb_device) {
- const char* platform_device = "/devices/platform/some_device_name";
- Uevent uevent = {
- .path = "/devices/platform/some_device_name/usb/usb_device/", .subsystem = "tty",
- };
- std::vector<std::string> expected_result;
-
- DeviceHandlerTester device_handler_tester_;
- device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
-}
-
-TEST(device_handler, get_character_device_symlinks_no_final_slash) {
- const char* platform_device = "/devices/platform/some_device_name";
- Uevent uevent = {
- .path = "/devices/platform/some_device_name/usb/usb_device/name", .subsystem = "tty",
- };
- std::vector<std::string> expected_result;
-
- DeviceHandlerTester device_handler_tester_;
- device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
-}
-
-TEST(device_handler, get_character_device_symlinks_no_final_name) {
- const char* platform_device = "/devices/platform/some_device_name";
- Uevent uevent = {
- .path = "/devices/platform/some_device_name/usb/usb_device//", .subsystem = "tty",
- };
- std::vector<std::string> expected_result;
-
- DeviceHandlerTester device_handler_tester_;
- device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, false);
-}
-
TEST(device_handler, get_block_device_symlinks_success_platform) {
// These are actual paths from bullhead
const char* platform_device = "/devices/soc.0/f9824900.sdhci";
@@ -164,7 +71,7 @@
std::vector<std::string> expected_result{"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0"};
DeviceHandlerTester device_handler_tester_;
- device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
}
TEST(device_handler, get_block_device_symlinks_success_platform_with_partition) {
@@ -182,7 +89,7 @@
};
DeviceHandlerTester device_handler_tester_;
- device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
}
TEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_num) {
@@ -198,7 +105,7 @@
};
DeviceHandlerTester device_handler_tester_;
- device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
}
TEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_name) {
@@ -214,7 +121,7 @@
};
DeviceHandlerTester device_handler_tester_;
- device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
}
TEST(device_handler, get_block_device_symlinks_success_pci) {
@@ -225,7 +132,7 @@
std::vector<std::string> expected_result{"/dev/block/pci/pci0000:00/0000:00:1f.2/mmcblk0"};
DeviceHandlerTester device_handler_tester_;
- device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
}
TEST(device_handler, get_block_device_symlinks_pci_bad_format) {
@@ -236,7 +143,7 @@
std::vector<std::string> expected_result{};
DeviceHandlerTester device_handler_tester_;
- device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
}
TEST(device_handler, get_block_device_symlinks_success_vbd) {
@@ -247,7 +154,7 @@
std::vector<std::string> expected_result{"/dev/block/vbd/1234/mmcblk0"};
DeviceHandlerTester device_handler_tester_;
- device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
}
TEST(device_handler, get_block_device_symlinks_vbd_bad_format) {
@@ -258,7 +165,7 @@
std::vector<std::string> expected_result{};
DeviceHandlerTester device_handler_tester_;
- device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
}
TEST(device_handler, get_block_device_symlinks_no_matches) {
@@ -271,7 +178,7 @@
std::vector<std::string> expected_result;
DeviceHandlerTester device_handler_tester_;
- device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result, true);
+ device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);
}
TEST(device_handler, sanitize_null) {
diff --git a/init/firmware_handler.cpp b/init/firmware_handler.cpp
index 8cd5cc5..b686885 100644
--- a/init/firmware_handler.cpp
+++ b/init/firmware_handler.cpp
@@ -24,12 +24,12 @@
#include <string>
#include <thread>
+#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
-#include "util.h"
-
+using android::base::Timer;
using android::base::unique_fd;
using android::base::WriteFully;
@@ -110,31 +110,16 @@
if (uevent.subsystem != "firmware" || uevent.action != "add") return;
// Loading the firmware in a child means we can do that in parallel...
- // We double fork instead of waiting for these processes.
- pid_t pid = fork();
+ auto pid = fork();
if (pid == -1) {
PLOG(ERROR) << "could not fork to process firmware event for " << uevent.firmware;
- return;
}
-
if (pid == 0) {
- pid = fork();
- if (pid == -1) {
- PLOG(ERROR) << "could not fork a sceond time to process firmware event for "
- << uevent.firmware;
- _exit(EXIT_FAILURE);
- }
- if (pid == 0) {
- Timer t;
- ProcessFirmwareEvent(uevent);
- LOG(INFO) << "loading " << uevent.path << " took " << t;
- _exit(EXIT_SUCCESS);
- }
-
+ Timer t;
+ ProcessFirmwareEvent(uevent);
+ LOG(INFO) << "loading " << uevent.path << " took " << t;
_exit(EXIT_SUCCESS);
}
-
- waitpid(pid, nullptr, 0);
}
} // namespace init
diff --git a/init/init.cpp b/init/init.cpp
index 0562dad..bf251ff 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -73,6 +73,7 @@
using android::base::boot_clock;
using android::base::GetProperty;
+using android::base::Timer;
namespace android {
namespace init {
@@ -94,6 +95,7 @@
static std::unique_ptr<Timer> waiting_for_prop(nullptr);
static std::string wait_prop_name;
static std::string wait_prop_value;
+static bool shutting_down;
void DumpState() {
ServiceManager::GetInstance().DumpState();
@@ -158,21 +160,31 @@
return true;
}
+void ResetWaitForProp() {
+ wait_prop_name.clear();
+ wait_prop_value.clear();
+ waiting_for_prop.reset();
+}
+
void property_changed(const std::string& name, const std::string& value) {
// If the property is sys.powerctl, we bypass the event queue and immediately handle it.
// This is to ensure that init will always and immediately shutdown/reboot, regardless of
// if there are other pending events to process or if init is waiting on an exec service or
// waiting on a property.
- if (name == "sys.powerctl") HandlePowerctlMessage(value);
+ // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
+ // commands to be executed.
+ if (name == "sys.powerctl") {
+ if (HandlePowerctlMessage(value)) {
+ shutting_down = true;
+ }
+ }
if (property_triggers_enabled) ActionManager::GetInstance().QueuePropertyChange(name, value);
if (waiting_for_prop) {
if (wait_prop_name == name && wait_prop_value == value) {
- wait_prop_name.clear();
- wait_prop_value.clear();
LOG(INFO) << "Wait for property took " << *waiting_for_prop;
- waiting_for_prop.reset();
+ ResetWaitForProp();
}
}
}
@@ -221,7 +233,7 @@
panic();
}
- property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration_ms()));
+ property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration().count()));
return 0;
}
@@ -859,7 +871,7 @@
}
// init's first stage can't set properties, so pass the time to the second stage.
- setenv("INIT_SELINUX_TOOK", std::to_string(t.duration_ms()).c_str(), 1);
+ setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1);
} else {
selinux_init_all_handles();
}
@@ -1135,7 +1147,7 @@
am.ExecuteOneCommand();
}
if (!(waiting_for_prop || sm.IsWaitingForExec())) {
- restart_processes();
+ if (!shutting_down) restart_processes();
// If there's a process that needs restarting, wake up in time for that.
if (process_needs_restart_at != 0) {
diff --git a/init/init.h b/init/init.h
index 479b771..aaab523 100644
--- a/init/init.h
+++ b/init/init.h
@@ -44,6 +44,8 @@
void DumpState();
+void ResetWaitForProp();
+
} // namespace init
} // namespace android
diff --git a/init/init_first_stage.cpp b/init/init_first_stage.cpp
index e10c3b2..0f7e38f 100644
--- a/init/init_first_stage.cpp
+++ b/init/init_first_stage.cpp
@@ -25,6 +25,7 @@
#include <string>
#include <vector>
+#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
@@ -36,7 +37,7 @@
#include "uevent_listener.h"
#include "util.h"
-using namespace std::chrono_literals;
+using android::base::Timer;
namespace android {
namespace init {
@@ -171,10 +172,13 @@
};
uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
if (!found) {
+ LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
+ Timer t;
uevent_listener_.Poll(dm_callback, 10s);
+ LOG(INFO) << "Wait for device-mapper returned after " << t;
}
if (!found) {
- LOG(ERROR) << "device-mapper device not found";
+ LOG(ERROR) << "device-mapper device not found after polling timeout";
return false;
}
}
@@ -185,11 +189,16 @@
// UeventCallback() will remove found partitions from required_devices_partition_names_.
// So if it isn't empty here, it means some partitions are not found.
if (!required_devices_partition_names_.empty()) {
+ LOG(INFO) << __PRETTY_FUNCTION__
+ << ": partition(s) not found in /sys, waiting for their uevent(s): "
+ << android::base::Join(required_devices_partition_names_, ", ");
+ Timer t;
uevent_listener_.Poll(uevent_callback, 10s);
+ LOG(INFO) << "Wait for partitions returned after " << t;
}
if (!required_devices_partition_names_.empty()) {
- LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found: "
+ LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
<< android::base::Join(required_devices_partition_names_, ", ");
return false;
}
@@ -241,10 +250,13 @@
uevent_listener_.RegenerateUeventsForPath(syspath, verity_callback);
if (!found) {
+ LOG(INFO) << "dm-verity device not found in /sys, waiting for its uevent";
+ Timer t;
uevent_listener_.Poll(verity_callback, 10s);
+ LOG(INFO) << "wait for dm-verity device returned after " << t;
}
if (!found) {
- LOG(ERROR) << "dm-verity device not found";
+ LOG(ERROR) << "dm-verity device not found after polling timeout";
return false;
}
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index e76d589..9f7089b 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -18,6 +18,7 @@
#include <dirent.h>
+#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -111,7 +112,7 @@
bool Parser::ParseConfigFile(const std::string& path) {
LOG(INFO) << "Parsing file " << path << "...";
- Timer t;
+ android::base::Timer t;
std::string data;
std::string err;
if (!ReadFile(path, &data, &err)) {
diff --git a/init/init_parser_test.cpp b/init/init_parser_test.cpp
index 38b8275..95f269a 100644
--- a/init/init_parser_test.cpp
+++ b/init/init_parser_test.cpp
@@ -16,13 +16,14 @@
#include "init_parser.h"
-#include "init.h"
-#include "service.h"
+#include <string>
+#include <vector>
#include <gtest/gtest.h>
-#include <string>
-#include <vector>
+#include "init.h"
+#include "service.h"
+#include "util.h"
namespace android {
namespace init {
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 0af6c21..925cc9b 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -42,6 +42,7 @@
#include <queue>
#include <vector>
+#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
@@ -55,6 +56,8 @@
#include "init.h"
#include "util.h"
+using android::base::Timer;
+
#define PERSISTENT_PROPERTY_DIR "/data/property"
#define RECOVERY_MOUNT_POINT "/recovery"
@@ -350,7 +353,7 @@
while (*timeout_ms > 0) {
Timer timer;
int nr = poll(ufds, 1, *timeout_ms);
- uint64_t millis = timer.duration_ms();
+ uint64_t millis = timer.duration().count();
*timeout_ms = (millis > *timeout_ms) ? 0 : *timeout_ms - millis;
if (nr > 0) {
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 34c98a7..969caec 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -35,6 +35,7 @@
#include <thread>
#include <vector>
+#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/macros.h>
@@ -49,10 +50,12 @@
#include <private/android_filesystem_config.h>
#include "capabilities.h"
+#include "init.h"
#include "property_service.h"
#include "service.h"
using android::base::StringPrintf;
+using android::base::Timer;
namespace android {
namespace init {
@@ -160,7 +163,8 @@
}
static void LogShutdownTime(UmountStat stat, Timer* t) {
- LOG(WARNING) << "powerctl_shutdown_time_ms:" << std::to_string(t->duration_ms()) << ":" << stat;
+ LOG(WARNING) << "powerctl_shutdown_time_ms:" << std::to_string(t->duration().count()) << ":"
+ << stat;
}
// Determines whether the system is capable of rebooting. This is conservative,
@@ -253,7 +257,7 @@
}
}
-static UmountStat UmountPartitions(int timeoutMs) {
+static UmountStat UmountPartitions(std::chrono::milliseconds timeout) {
Timer t;
UmountStat stat = UMOUNT_STAT_TIMEOUT;
int retry = 0;
@@ -271,7 +275,7 @@
stat = UMOUNT_STAT_SUCCESS;
break;
}
- if ((timeoutMs < t.duration_ms()) && retry > 0) { // try umount at least once
+ if ((timeout < t.duration()) && retry > 0) { // try umount at least once
stat = UMOUNT_STAT_TIMEOUT;
break;
}
@@ -300,7 +304,7 @@
*
* return true when umount was successful. false when timed out.
*/
-static UmountStat TryUmountAndFsck(bool runFsck, int timeoutMs) {
+static UmountStat TryUmountAndFsck(bool runFsck, std::chrono::milliseconds timeout) {
Timer t;
std::vector<MountEntry> block_devices;
std::vector<MountEntry> emulated_devices;
@@ -311,13 +315,13 @@
return UMOUNT_STAT_ERROR;
}
- UmountStat stat = UmountPartitions(timeoutMs - t.duration_ms());
+ UmountStat stat = UmountPartitions(timeout - t.duration());
if (stat != UMOUNT_STAT_SUCCESS) {
LOG(INFO) << "umount timeout, last resort, kill all and try";
if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(false);
KillAllProcesses();
// even if it succeeds, still it is timeout and do not run fsck with all processes killed
- UmountPartitions(0);
+ UmountPartitions(0ms);
if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo(true);
}
@@ -351,26 +355,27 @@
abort();
}
- constexpr unsigned int shutdownTimeoutDefault = 6;
- unsigned int shutdownTimeout = shutdownTimeoutDefault;
- if (SHUTDOWN_ZERO_TIMEOUT) { // eng build
- shutdownTimeout = 0;
- } else {
- shutdownTimeout =
- android::base::GetUintProperty("ro.build.shutdown_timeout", shutdownTimeoutDefault);
+ auto shutdown_timeout = 0s;
+ if (!SHUTDOWN_ZERO_TIMEOUT) {
+ constexpr unsigned int shutdown_timeout_default = 6;
+ auto shutdown_timeout_property =
+ android::base::GetUintProperty("ro.build.shutdown_timeout", shutdown_timeout_default);
+ shutdown_timeout = std::chrono::seconds(shutdown_timeout_property);
}
- LOG(INFO) << "Shutdown timeout: " << shutdownTimeout;
+ LOG(INFO) << "Shutdown timeout: " << shutdown_timeout.count() << " seconds";
// keep debugging tools until non critical ones are all gone.
const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
// watchdogd is a vendor specific component but should be alive to complete shutdown safely.
- const std::set<std::string> to_starts{"watchdogd", "vold", "ueventd"};
+ const std::set<std::string> to_starts{"watchdogd"};
ServiceManager::GetInstance().ForEachService([&kill_after_apps, &to_starts](Service* s) {
if (kill_after_apps.count(s->name())) {
s->SetShutdownCritical();
} else if (to_starts.count(s->name())) {
s->Start();
s->SetShutdownCritical();
+ } else if (s->IsShutdownCritical()) {
+ s->Start(); // start shutdown critical service if not started
}
});
@@ -384,7 +389,7 @@
// optional shutdown step
// 1. terminate all services except shutdown critical ones. wait for delay to finish
- if (shutdownTimeout > 0) {
+ if (shutdown_timeout > 0s) {
LOG(INFO) << "terminating init services";
// Ask all services to terminate except shutdown critical ones.
@@ -393,9 +398,9 @@
});
int service_count = 0;
- // Up to half as long as shutdownTimeout or 3 seconds, whichever is lower.
- unsigned int terminationWaitTimeout = std::min<unsigned int>((shutdownTimeout + 1) / 2, 3);
- while (t.duration_s() < terminationWaitTimeout) {
+ // Up to half as long as shutdown_timeout or 3 seconds, whichever is lower.
+ auto termination_wait_timeout = std::min((shutdown_timeout + 1s) / 2, 3s);
+ while (t.duration() < termination_wait_timeout) {
ServiceManager::GetInstance().ReapAnyOutstandingChildren();
service_count = 0;
@@ -443,7 +448,7 @@
});
// 4. sync, try umount, and optionally run fsck for user shutdown
sync();
- UmountStat stat = TryUmountAndFsck(runFsck, shutdownTimeout * 1000 - t.duration_ms());
+ UmountStat stat = TryUmountAndFsck(runFsck, shutdown_timeout - t.duration());
// Follow what linux shutdown is doing: one more sync with little bit delay
sync();
std::this_thread::sleep_for(100ms);
@@ -490,6 +495,9 @@
}
} else if (command == "thermal-shutdown") { // no additional parameter allowed
cmd = ANDROID_RB_THERMOFF;
+ // Do not queue "shutdown" trigger since we want to shutdown immediately
+ DoReboot(cmd, command, reboot_target, run_fsck);
+ return true;
} else {
command_invalid = true;
}
@@ -498,7 +506,26 @@
return false;
}
- DoReboot(cmd, command, reboot_target, run_fsck);
+ LOG(INFO) << "Clear action queue and start shutdown trigger";
+ ActionManager::GetInstance().ClearQueue();
+ // Queue shutdown trigger first
+ ActionManager::GetInstance().QueueEventTrigger("shutdown");
+ // Queue built-in shutdown_done
+ auto shutdown_handler = [cmd, command, reboot_target,
+ run_fsck](const std::vector<std::string>&) {
+ DoReboot(cmd, command, reboot_target, run_fsck);
+ return 0;
+ };
+ ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, "shutdown_done");
+
+ // Skip wait for prop if it is in progress
+ ResetWaitForProp();
+
+ // Skip wait for exec if it is in progress
+ if (ServiceManager::GetInstance().IsWaitingForExec()) {
+ ServiceManager::GetInstance().ClearExecWait();
+ }
+
return true;
}
diff --git a/init/service.cpp b/init/service.cpp
index f2e5d22..7a657c8 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -34,6 +34,7 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <processgroup/processgroup.h>
@@ -47,6 +48,7 @@
using android::base::boot_clock;
using android::base::GetProperty;
using android::base::Join;
+using android::base::make_scope_guard;
using android::base::ParseInt;
using android::base::StartsWith;
using android::base::StringPrintf;
@@ -499,6 +501,14 @@
return true;
}
+bool Service::ParseShutdown(const std::vector<std::string>& args, std::string* err) {
+ if (args[1] == "critical") {
+ flags_ |= SVC_SHUTDOWN_CRITICAL;
+ return true;
+ }
+ return false;
+}
+
template <typename T>
bool Service::AddDescriptor(const std::vector<std::string>& args, std::string* err) {
int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
@@ -602,6 +612,7 @@
{"namespace", {1, 2, &Service::ParseNamespace}},
{"seclabel", {1, 1, &Service::ParseSeclabel}},
{"setenv", {2, 2, &Service::ParseSetenv}},
+ {"shutdown", {1, 1, &Service::ParseShutdown}},
{"socket", {3, 6, &Service::ParseSocket}},
{"file", {2, 2, &Service::ParseFile}},
{"user", {1, 1, &Service::ParseUser}},
@@ -622,10 +633,10 @@
return (this->*parser)(args, err);
}
-bool Service::ExecStart(std::unique_ptr<Timer>* exec_waiter) {
+bool Service::ExecStart(std::unique_ptr<android::base::Timer>* exec_waiter) {
flags_ |= SVC_EXEC | SVC_ONESHOT;
- exec_waiter->reset(new Timer);
+ exec_waiter->reset(new android::base::Timer);
if (!Start()) {
exec_waiter->reset();
@@ -1088,14 +1099,24 @@
}
bool ServiceManager::ReapOneProcess() {
- int status;
- pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, WNOHANG));
- if (pid == 0) {
+ siginfo_t siginfo = {};
+ // This returns a zombie pid or informs us that there are no zombies left to be reaped.
+ // It does NOT reap the pid; that is done below.
+ if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
+ PLOG(ERROR) << "waitid failed";
return false;
- } else if (pid == -1) {
- PLOG(ERROR) << "waitpid failed";
- return false;
- } else if (PropertyChildReap(pid)) {
+ }
+
+ auto pid = siginfo.si_pid;
+ if (pid == 0) return false;
+
+ // At this point we know we have a zombie pid, so we use this scopeguard to reap the pid
+ // whenever the function returns from this point forward.
+ // We do NOT want to reap the zombie earlier as in Service::Reap(), we kill(-pid, ...) and we
+ // want the pid to remain valid throughout that (and potentially future) usages.
+ auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });
+
+ if (PropertyChildReap(pid)) {
return true;
}
@@ -1106,20 +1127,18 @@
if (svc) {
name = StringPrintf("Service '%s' (pid %d)", svc->name().c_str(), pid);
if (svc->flags() & SVC_EXEC) {
- wait_string = StringPrintf(" waiting took %f seconds", exec_waiter_->duration_s());
+ wait_string = StringPrintf(" waiting took %f seconds",
+ exec_waiter_->duration().count() / 1000.0f);
}
} else {
name = StringPrintf("Untracked pid %d", pid);
}
+ auto status = siginfo.si_status;
if (WIFEXITED(status)) {
LOG(INFO) << name << " exited with status " << WEXITSTATUS(status) << wait_string;
} else if (WIFSIGNALED(status)) {
LOG(INFO) << name << " killed by signal " << WTERMSIG(status) << wait_string;
- } else if (WIFSTOPPED(status)) {
- LOG(INFO) << name << " stopped by signal " << WSTOPSIG(status) << wait_string;
- } else {
- LOG(INFO) << name << " state changed" << wait_string;
}
if (!svc) {
@@ -1143,6 +1162,15 @@
}
}
+void ServiceManager::ClearExecWait() {
+ // Clear EXEC flag if there is one pending
+ // And clear the wait flag
+ for (const auto& s : services_) {
+ s->UnSetExec();
+ }
+ exec_waiter_.reset();
+}
+
bool ServiceParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
int line, std::string* err) {
if (args.size() < 3) {
diff --git a/init/service.h b/init/service.h
index 3c7dc74..f682abd 100644
--- a/init/service.h
+++ b/init/service.h
@@ -32,7 +32,6 @@
#include "descriptors.h"
#include "init_parser.h"
#include "keyword_map.h"
-#include "util.h"
#define SVC_DISABLED 0x001 // do not autostart with class
#define SVC_ONESHOT 0x002 // do not restart on exit
@@ -76,7 +75,7 @@
bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
bool ParseLine(const std::vector<std::string>& args, std::string* err);
- bool ExecStart(std::unique_ptr<Timer>* exec_waiter);
+ bool ExecStart(std::unique_ptr<android::base::Timer>* exec_waiter);
bool Start();
bool StartIfNotDisabled();
bool Enable();
@@ -89,6 +88,7 @@
void DumpState() const;
void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; }
bool IsShutdownCritical() const { return (flags_ & SVC_SHUTDOWN_CRITICAL) != 0; }
+ void UnSetExec() { flags_ &= ~SVC_EXEC; }
const std::string& name() const { return name_; }
const std::set<std::string>& classnames() const { return classnames_; }
@@ -137,6 +137,7 @@
bool ParseNamespace(const std::vector<std::string>& args, std::string* err);
bool ParseSeclabel(const std::vector<std::string>& args, std::string* err);
bool ParseSetenv(const std::vector<std::string>& args, std::string* err);
+ bool ParseShutdown(const std::vector<std::string>& args, std::string* err);
bool ParseSocket(const std::vector<std::string>& args, std::string* err);
bool ParseFile(const std::vector<std::string>& args, std::string* err);
bool ParseUser(const std::vector<std::string>& args, std::string* err);
@@ -186,7 +187,7 @@
};
class ServiceManager {
-public:
+ public:
static ServiceManager& GetInstance();
// Exposed for testing
@@ -208,14 +209,15 @@
void ReapAnyOutstandingChildren();
void RemoveService(const Service& svc);
void DumpState() const;
+ void ClearExecWait();
-private:
+ private:
// Cleans up a child process that exited.
// Returns true iff a children was cleaned up.
bool ReapOneProcess();
static int exec_count_; // Every service needs a unique name.
- std::unique_ptr<Timer> exec_waiter_;
+ std::unique_ptr<android::base::Timer> exec_waiter_;
std::vector<std::unique_ptr<Service>> services_;
};
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 81a0572..c0eae1e 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -27,6 +27,7 @@
#include <set>
#include <thread>
+#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <selinux/android.h>
@@ -198,7 +199,7 @@
}
void ColdBoot::Run() {
- Timer cold_boot_timer;
+ android::base::Timer cold_boot_timer;
RegenerateUevents();
@@ -209,7 +210,7 @@
WaitForSubProcesses();
close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
- LOG(INFO) << "Coldboot took " << cold_boot_timer;
+ LOG(INFO) << "Coldboot took " << cold_boot_timer.duration().count() / 1000.0f << " seconds";
}
DeviceHandler CreateDeviceHandler() {
@@ -268,6 +269,13 @@
cold_boot.Run();
}
+ // We use waitpid() in ColdBoot, so we can't ignore SIGCHLD until now.
+ signal(SIGCHLD, SIG_IGN);
+ // Reap and pending children that exited between the last call to waitpid() and setting SIG_IGN
+ // for SIGCHLD above.
+ while (waitpid(-1, nullptr, WNOHANG) > 0) {
+ }
+
uevent_listener.Poll([&device_handler](const Uevent& uevent) {
HandleFirmwareEvent(uevent);
device_handler.HandleDeviceEvent(uevent);
diff --git a/init/util.cpp b/init/util.cpp
index 4b1894f..2792794 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -370,14 +370,10 @@
void panic() {
LOG(ERROR) << "panic: rebooting to bootloader";
+ // Do not queue "shutdown" trigger since we want to shutdown immediately
DoReboot(ANDROID_RB_RESTART2, "reboot", "bootloader", false);
}
-std::ostream& operator<<(std::ostream& os, const Timer& t) {
- os << t.duration_s() << " seconds";
- return os;
-}
-
// Reads the content of device tree file under kAndroidDtDir directory.
// Returns true if the read is success, false otherwise.
bool read_android_dt_file(const std::string& sub_path, std::string* dt_content) {
diff --git a/init/util.h b/init/util.h
index 346953f..452df2d 100644
--- a/init/util.h
+++ b/init/util.h
@@ -44,26 +44,6 @@
bool ReadFile(const std::string& path, std::string* content, std::string* err);
bool WriteFile(const std::string& path, const std::string& content, std::string* err);
-class Timer {
- public:
- Timer() : start_(boot_clock::now()) {}
-
- double duration_s() const {
- typedef std::chrono::duration<double> double_duration;
- return std::chrono::duration_cast<double_duration>(boot_clock::now() - start_).count();
- }
-
- int64_t duration_ms() const {
- return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_)
- .count();
- }
-
- private:
- android::base::boot_clock::time_point start_;
-};
-
-std::ostream& operator<<(std::ostream& os, const Timer& t);
-
bool DecodeUid(const std::string& name, uid_t* uid, std::string* err);
int mkdir_recursive(const std::string& pathname, mode_t mode, selabel_handle* sehandle);
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index a643a29..e02aaf2 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -161,6 +161,7 @@
shared_libs = [
"libbase",
"libunwind",
+ "libziparchive",
],
}
diff --git a/liblog/Android.bp b/liblog/Android.bp
index e74aa82..b98d18f 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -42,6 +42,24 @@
"logd_writer.c",
]
+cc_library_headers {
+ name: "liblog_headers",
+ host_supported: true,
+ vendor_available: true,
+ export_include_dirs: ["include"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ linux_bionic: {
+ enabled: true,
+ },
+ vendor: {
+ export_include_dirs: ["include_vndk"],
+ },
+ },
+}
+
// Shared and static library for host and device
// ========================================================
cc_library {
@@ -81,7 +99,8 @@
},
},
- export_include_dirs: ["include"],
+ header_libs: ["liblog_headers"],
+ export_header_lib_headers: ["liblog_headers"],
cflags: [
"-Werror",
@@ -100,7 +119,7 @@
}
ndk_headers {
- name: "liblog_headers",
+ name: "liblog_ndk_headers",
from: "include/android",
to: "android",
srcs: ["include/android/log.h"],
diff --git a/liblog/include/log/log.h b/liblog/include/log/log.h
index 3a215e9..d01708d 100644
--- a/liblog/include/log/log.h
+++ b/liblog/include/log/log.h
@@ -161,7 +161,7 @@
#endif
#if __ANDROID_USE_LIBLOG_CLOCK_INTERFACE
-clockid_t android_log_clockid();
+clockid_t android_log_clockid(void);
#endif
#endif /* __linux__ */
@@ -185,7 +185,7 @@
* May be used to clean up File descriptors after a Fork, the resources are
* all O_CLOEXEC so wil self clean on exec().
*/
-void __android_log_close();
+void __android_log_close(void);
#endif
#ifndef __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp
index cdac76b..826a576 100644
--- a/libmemunreachable/Android.bp
+++ b/libmemunreachable/Android.bp
@@ -26,6 +26,7 @@
defaults: ["libmemunreachable_defaults"],
srcs: [
"Allocator.cpp",
+ "Binder.cpp",
"HeapWalker.cpp",
"LeakFolding.cpp",
"LeakPipe.cpp",
@@ -84,3 +85,18 @@
},
},
}
+
+cc_test {
+ name: "memunreachable_binder_test",
+ defaults: ["libmemunreachable_defaults"],
+ srcs: [
+ "tests/Binder_test.cpp",
+ "tests/MemUnreachable_test.cpp",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libhwbinder",
+ "libmemunreachable",
+ "libutils",
+ ],
+}
diff --git a/libmemunreachable/Binder.cpp b/libmemunreachable/Binder.cpp
new file mode 100644
index 0000000..60512a3
--- /dev/null
+++ b/libmemunreachable/Binder.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/cdefs.h>
+#include <unistd.h>
+
+#include <functional>
+
+#include "Binder.h"
+#include "log.h"
+
+__BEGIN_DECLS
+
+// Weak undefined references to the symbols in libbinder and libhwbinder
+// so that libmemunreachable can call them in processes that have them
+// loaded without requiring libmemunreachable to have dependencies on them.
+ssize_t __attribute__((weak)) getBinderKernelReferences(size_t, uintptr_t*);
+ssize_t __attribute__((weak)) getHWBinderKernelReferences(size_t, uintptr_t*);
+
+__END_DECLS
+
+namespace android {
+
+static bool BinderReferencesToVector(allocator::vector<uintptr_t>& refs,
+ std::function<ssize_t(size_t, uintptr_t*)> fn) {
+ if (fn == nullptr) {
+ return true;
+ }
+
+ size_t size = refs.size();
+
+ do {
+ refs.resize(size);
+
+ ssize_t ret = fn(refs.size(), refs.data());
+ if (ret < 0) {
+ return false;
+ }
+
+ size = ret;
+ } while (size > refs.size());
+
+ refs.resize(size);
+ return true;
+}
+
+bool BinderReferences(allocator::vector<uintptr_t>& refs) {
+ refs.clear();
+
+ allocator::vector<uintptr_t> binder_refs{refs.get_allocator()};
+ if (BinderReferencesToVector(refs, getBinderKernelReferences)) {
+ refs.insert(refs.end(), binder_refs.begin(), binder_refs.end());
+ } else {
+ MEM_ALOGE("getBinderKernelReferences failed");
+ }
+
+ allocator::vector<uintptr_t> hwbinder_refs{refs.get_allocator()};
+ if (BinderReferencesToVector(hwbinder_refs, getHWBinderKernelReferences)) {
+ refs.insert(refs.end(), hwbinder_refs.begin(), hwbinder_refs.end());
+ } else {
+ MEM_ALOGE("getHWBinderKernelReferences failed");
+ }
+
+ return true;
+}
+
+} // namespace android
diff --git a/libmemunreachable/Binder.h b/libmemunreachable/Binder.h
new file mode 100644
index 0000000..bf4fd3e
--- /dev/null
+++ b/libmemunreachable/Binder.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBMEMUNREACHABLE_BINDER_H_
+#define LIBMEMUNREACHABLE_BINDER_H_
+
+#include "Allocator.h"
+
+namespace android {
+
+bool BinderReferences(allocator::vector<uintptr_t>& refs);
+
+} // namespace android
+
+#endif // LIBMEMUNREACHABLE_BINDER_H_
diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp
index a1f74c3..5e062fd 100644
--- a/libmemunreachable/MemUnreachable.cpp
+++ b/libmemunreachable/MemUnreachable.cpp
@@ -28,6 +28,7 @@
#include <backtrace.h>
#include "Allocator.h"
+#include "Binder.h"
#include "HeapWalker.h"
#include "Leak.h"
#include "LeakFolding.h"
@@ -53,7 +54,8 @@
MemUnreachable(pid_t pid, Allocator<void> allocator)
: pid_(pid), allocator_(allocator), heap_walker_(allocator_) {}
bool CollectAllocations(const allocator::vector<ThreadInfo>& threads,
- const allocator::vector<Mapping>& mappings);
+ const allocator::vector<Mapping>& mappings,
+ const allocator::vector<uintptr_t>& refs);
bool GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit, size_t* num_leaks,
size_t* leak_bytes);
size_t Allocations() { return heap_walker_.Allocations(); }
@@ -82,7 +84,8 @@
}
bool MemUnreachable::CollectAllocations(const allocator::vector<ThreadInfo>& threads,
- const allocator::vector<Mapping>& mappings) {
+ const allocator::vector<Mapping>& mappings,
+ const allocator::vector<uintptr_t>& refs) {
MEM_ALOGI("searching process %d for allocations", pid_);
allocator::vector<Mapping> heap_mappings{mappings};
allocator::vector<Mapping> anon_mappings{mappings};
@@ -118,6 +121,8 @@
heap_walker_.Root(thread_it->regs);
}
+ heap_walker_.Root(refs);
+
MEM_ALOGI("searching done");
return true;
@@ -282,6 +287,7 @@
ThreadCapture thread_capture(parent_pid, heap);
allocator::vector<ThreadInfo> thread_info(heap);
allocator::vector<Mapping> mappings(heap);
+ allocator::vector<uintptr_t> refs(heap);
// ptrace all the threads
if (!thread_capture.CaptureThreads()) {
@@ -301,6 +307,11 @@
return 1;
}
+ if (!BinderReferences(refs)) {
+ continue_parent_sem.Post();
+ return 1;
+ }
+
// malloc must be enabled to call fork, at_fork handlers take the same
// locks as ScopedDisableMalloc. All threads are paused in ptrace, so
// memory state is still consistent. Unfreeze the original thread so it
@@ -326,7 +337,7 @@
MemUnreachable unreachable{parent_pid, heap};
- if (!unreachable.CollectAllocations(thread_info, mappings)) {
+ if (!unreachable.CollectAllocations(thread_info, mappings, refs)) {
_exit(2);
}
size_t num_allocations = unreachable.Allocations();
diff --git a/libmemunreachable/tests/AndroidTest.xml b/libmemunreachable/tests/AndroidTest.xml
new file mode 100644
index 0000000..604c0ec
--- /dev/null
+++ b/libmemunreachable/tests/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for memunreachable_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="memunreachable_test->/data/local/tmp/memunreachable_test" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="memunreachable_test" />
+ </test>
+</configuration>
diff --git a/libmemunreachable/tests/Binder_test.cpp b/libmemunreachable/tests/Binder_test.cpp
new file mode 100644
index 0000000..6e85d5a
--- /dev/null
+++ b/libmemunreachable/tests/Binder_test.cpp
@@ -0,0 +1,156 @@
+/*
+ * 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 <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <binder/Binder.h>
+#include <binder/IBinder.h>
+#include <binder/IServiceManager.h>
+#include <binder/Parcel.h>
+#include <binder/ProcessState.h>
+
+#include <gtest/gtest.h>
+
+#include "Allocator.h"
+#include "Binder.h"
+
+namespace android {
+
+static const String16 service_name("test.libmemunreachable_binder");
+
+class BinderService : public BBinder {
+ public:
+ BinderService() = default;
+ virtual ~BinderService() = default;
+
+ virtual status_t onTransact(uint32_t /*code*/, const Parcel& data, Parcel* reply,
+ uint32_t /*flags*/ = 0) {
+ reply->writeStrongBinder(ref);
+ ref = data.readStrongBinder();
+ return 0;
+ }
+
+ private:
+ sp<IBinder> ref;
+};
+
+class BinderObject : public BBinder {
+ public:
+ BinderObject() = default;
+ ~BinderObject() = default;
+};
+
+class ServiceProcess {
+ public:
+ ServiceProcess() : child_(0) {}
+ ~ServiceProcess() { Stop(); }
+
+ bool Run() {
+ pid_t ret = fork();
+ if (ret < 0) {
+ return false;
+ } else if (ret == 0) {
+ // child
+ _exit(Service());
+ } else {
+ // parent
+ child_ = ret;
+ return true;
+ }
+ }
+
+ bool Stop() {
+ if (child_ > 0) {
+ if (kill(child_, SIGTERM)) {
+ return false;
+ }
+ int status = 0;
+ if (TEMP_FAILURE_RETRY(waitpid(child_, &status, 0)) != child_) {
+ return false;
+ }
+ child_ = 0;
+ return WIFEXITED(status) && WEXITSTATUS(status) == 0;
+ }
+
+ return true;
+ }
+
+ int Service() {
+ sp<ProcessState> proc{ProcessState::self()};
+ sp<IServiceManager> sm = defaultServiceManager();
+ if (sm == nullptr) {
+ fprintf(stderr, "Failed to get service manager\n");
+ return 1;
+ }
+ if (sm->addService(service_name, new BinderService()) != OK) {
+ fprintf(stderr, "Failed to add test service\n");
+ return 1;
+ }
+ proc->startThreadPool();
+ pause();
+ return 0;
+ }
+
+ private:
+ pid_t child_;
+};
+
+class BinderTest : public ::testing::Test {
+ protected:
+ ServiceProcess service_process_;
+};
+
+TEST_F(BinderTest, binder) {
+ ServiceProcess service_process;
+ ASSERT_TRUE(service_process.Run());
+
+ sp<IServiceManager> sm = defaultServiceManager();
+ ASSERT_TRUE(sm != nullptr);
+
+ // A small sleep allows the service to start, which
+ // prevents a longer sleep in getService.
+ usleep(100000);
+
+ sp<IBinder> service = sm->getService(service_name);
+ ASSERT_TRUE(service != nullptr);
+
+ sp<IBinder> binder{new BinderObject()};
+
+ Parcel send;
+ Parcel reply;
+
+ send.writeStrongBinder(binder);
+ status_t rv = service->transact(0, send, &reply);
+ ASSERT_EQ(static_cast<status_t>(OK), rv);
+
+ Heap heap;
+ allocator::vector<uintptr_t> refs{heap};
+
+ ASSERT_TRUE(BinderReferences(refs));
+
+ bool found_ref = false;
+ for (auto ref : refs) {
+ if (ref == reinterpret_cast<uintptr_t>(binder.get())) {
+ found_ref = true;
+ }
+ }
+
+ ASSERT_TRUE(found_ref);
+}
+
+} // namespace android
diff --git a/libnativebridge/Android.bp b/libnativebridge/Android.bp
index 377b7dd..1cea4cd 100644
--- a/libnativebridge/Android.bp
+++ b/libnativebridge/Android.bp
@@ -1,4 +1,11 @@
+cc_library_headers {
+ name: "libnativebridge-dummy-headers",
+
+ host_supported: true,
+ export_include_dirs=["include"],
+}
+
cc_library {
name: "libnativebridge",
@@ -7,6 +14,8 @@
shared_libs: ["liblog"],
clang: true,
+ export_include_dirs=["include"],
+
cflags: [
"-Werror",
"-Wall",
@@ -23,4 +32,4 @@
},
}
-subdirs = ["tests"]
\ No newline at end of file
+subdirs = ["tests"]
diff --git a/include/nativebridge/native_bridge.h b/libnativebridge/include/nativebridge/native_bridge.h
similarity index 100%
rename from include/nativebridge/native_bridge.h
rename to libnativebridge/include/nativebridge/native_bridge.h
diff --git a/libnativebridge/tests/Android.bp b/libnativebridge/tests/Android.bp
index efd3978..e31dae0 100644
--- a/libnativebridge/tests/Android.bp
+++ b/libnativebridge/tests/Android.bp
@@ -23,6 +23,7 @@
"-Wextra",
"-Werror",
],
+ header_libs: ["libnativebridge-dummy-headers"],
cppflags: ["-fvisibility=protected"],
target: {
android: {
diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp
index 6f76e76..87e2684 100644
--- a/libsysutils/src/FrameworkListener.cpp
+++ b/libsysutils/src/FrameworkListener.cpp
@@ -211,7 +211,6 @@
return;
overflow:
- LOG_EVENT_INT(78001, cli->getUid());
cli->sendMsg(500, "Command too long", false);
goto out;
}
diff --git a/libsysutils/src/NetlinkListener.cpp b/libsysutils/src/NetlinkListener.cpp
index 896dad3..aad0394 100644
--- a/libsysutils/src/NetlinkListener.cpp
+++ b/libsysutils/src/NetlinkListener.cpp
@@ -57,8 +57,6 @@
count = TEMP_FAILURE_RETRY(uevent_kernel_recv(socket,
mBuffer, sizeof(mBuffer), require_group, &uid));
if (count < 0) {
- if (uid > 0)
- LOG_EVENT_INT(65537, uid);
SLOGE("recvmsg failed (%s)", strerror(errno));
return false;
}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 8776db7..e6d3bff 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -31,18 +31,18 @@
},
},
- multilib: {
- lib32: {
- suffix: "32",
+ arch: {
+ mips: {
+ enabled: false,
},
- lib64: {
- suffix: "64",
+ mips64: {
+ enabled: false,
},
},
}
-cc_defaults {
- name: "libunwindstack_common",
+cc_library {
+ name: "libunwindstack",
defaults: ["libunwindstack_flags"],
srcs: [
@@ -64,6 +64,15 @@
"Symbols.cpp",
],
+ arch: {
+ x86: {
+ srcs: ["AsmGetRegsX86.S"],
+ },
+ x86_64: {
+ srcs: ["AsmGetRegsX86_64.S"],
+ },
+ },
+
shared_libs: [
"libbase",
"liblog",
@@ -71,16 +80,11 @@
],
}
-cc_library {
- name: "libunwindstack",
- defaults: ["libunwindstack_common"],
-}
-
//-------------------------------------------------------------------------
// Unit Tests
//-------------------------------------------------------------------------
-cc_defaults {
- name: "libunwindstack_test_common",
+cc_test {
+ name: "libunwindstack_test",
defaults: ["libunwindstack_flags"],
srcs: [
@@ -98,16 +102,21 @@
"tests/ElfInterfaceArmTest.cpp",
"tests/ElfInterfaceTest.cpp",
"tests/ElfTest.cpp",
+ "tests/ElfTestUtils.cpp",
"tests/LogFake.cpp",
- "tests/MapInfoTest.cpp",
+ "tests/MapInfoCreateMemoryTest.cpp",
+ "tests/MapInfoGetElfTest.cpp",
"tests/MapsTest.cpp",
+ "tests/MemoryBufferTest.cpp",
"tests/MemoryFake.cpp",
"tests/MemoryFileTest.cpp",
"tests/MemoryLocalTest.cpp",
"tests/MemoryRangeTest.cpp",
"tests/MemoryRemoteTest.cpp",
+ "tests/MemoryTest.cpp",
"tests/RegsTest.cpp",
"tests/SymbolsTest.cpp",
+ "tests/UnwindTest.cpp",
],
cflags: [
@@ -119,6 +128,7 @@
"libbase",
"liblog",
"liblzma",
+ "libunwindstack",
],
static_libs: [
@@ -132,28 +142,18 @@
],
},
},
-}
-
-// These unit tests run against the shared library.
-cc_test {
- name: "libunwindstack_test",
- defaults: ["libunwindstack_test_common"],
-
- shared_libs: [
- "libunwindstack",
- ],
data: [
- "tests/elf32.xz",
- "tests/elf64.xz",
+ "tests/files/elf32.xz",
+ "tests/files/elf64.xz",
],
}
//-------------------------------------------------------------------------
-// Utility Executables
+// Tools
//-------------------------------------------------------------------------
cc_defaults {
- name: "libunwindstack_executables",
+ name: "libunwindstack_tools",
defaults: ["libunwindstack_flags"],
shared_libs: [
@@ -161,20 +161,40 @@
"libbase",
"liblzma",
],
+}
- static_libs: [
- "liblog",
+cc_binary {
+ name: "unwind",
+ defaults: ["libunwindstack_tools"],
+
+ srcs: [
+ "tools/unwind.cpp",
],
- compile_multilib: "both",
+ target: {
+ linux: {
+ host_ldlibs: [
+ "-lrt",
+ ],
+ },
+ },
}
cc_binary {
name: "unwind_info",
- defaults: ["libunwindstack_executables"],
+ defaults: ["libunwindstack_tools"],
srcs: [
- "unwind_info.cpp",
+ "tools/unwind_info.cpp",
+ ],
+}
+
+cc_binary {
+ name: "unwind_symbols",
+ defaults: ["libunwindstack_tools"],
+
+ srcs: [
+ "tools/unwind_symbols.cpp",
],
}
@@ -182,20 +202,9 @@
// Once these files are generated, use the xz command to compress the data.
cc_binary_host {
name: "gen_gnudebugdata",
-
- cflags: [
- "-Wall",
- "-Werror",
- "-Wextra",
- ],
+ defaults: ["libunwindstack_flags"],
srcs: [
"tests/GenGnuDebugdata.cpp",
],
-
- target: {
- darwin: {
- enabled: false,
- },
- },
}
diff --git a/libunwindstack/AsmGetRegsX86.S b/libunwindstack/AsmGetRegsX86.S
new file mode 100644
index 0000000..14927a3
--- /dev/null
+++ b/libunwindstack/AsmGetRegsX86.S
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 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.
+ *
+ * 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.
+ */
+
+ .text
+ .global AsmGetRegs
+ .balign 16
+ .type AsmGetRegs, @function
+AsmGetRegs:
+ .cfi_startproc
+ mov 4(%esp), %eax
+ movl $0, (%eax)
+ movl %ecx, 4(%eax)
+ movl %edx, 8(%eax)
+ movl %ebx, 12(%eax)
+
+ /* ESP */
+ leal 4(%esp), %ecx
+ movl %ecx, 16(%eax)
+
+ movl %ebp, 20(%eax)
+ movl %esi, 24(%eax)
+ movl %edi, 28(%eax)
+
+ /* EIP */
+ movl (%esp), %ecx
+ movl %ecx, 32(%eax)
+
+ movl %cs, 36(%eax)
+ movl %ss, 40(%eax)
+ movl %ds, 44(%eax)
+ movl %es, 48(%eax)
+ movl %fs, 52(%eax)
+ movl %gs, 56(%eax)
+ ret
+
+ .cfi_endproc
+ .size AsmGetRegs, .-AsmGetRegs
diff --git a/libunwindstack/AsmGetRegsX86_64.S b/libunwindstack/AsmGetRegsX86_64.S
new file mode 100644
index 0000000..4cd3b6f
--- /dev/null
+++ b/libunwindstack/AsmGetRegsX86_64.S
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 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.
+ *
+ * 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.
+ */
+
+ .text
+ .global AsmGetRegs
+ .balign 16
+ .type AsmGetRegs, @function
+AsmGetRegs:
+ .cfi_startproc
+ movq %rax, (%rdi)
+ movq %rdx, 8(%rdi)
+ movq %rcx, 16(%rdi)
+ movq %rbx, 24(%rdi)
+ movq %rsi, 32(%rdi)
+ movq %rdi, 40(%rdi)
+ movq %rbp, 48(%rdi)
+
+ /* RSP */
+ lea 8(%rsp), %rax
+ movq %rax, 56(%rdi)
+
+ movq %r8, 64(%rdi)
+ movq %r9, 72(%rdi)
+ movq %r10, 80(%rdi)
+ movq %r11, 88(%rdi)
+ movq %r12, 96(%rdi)
+ movq %r13, 104(%rdi)
+ movq %r14, 112(%rdi)
+ movq %r15, 120(%rdi)
+
+ /* RIP */
+ movq (%rsp), %rax
+ movq %rax, 128(%rdi)
+ ret
+
+ .cfi_endproc
+ .size AsmGetRegs, .-AsmGetRegs
diff --git a/libunwindstack/Machine.h b/libunwindstack/Machine.h
index 323ce80..bcc3788 100644
--- a/libunwindstack/Machine.h
+++ b/libunwindstack/Machine.h
@@ -83,47 +83,51 @@
ARM64_REG_LR = ARM64_REG_R30,
};
+// Matches the numbers for the registers as generated by compilers.
+// If this is changed, then unwinding will fail.
enum X86Reg : uint16_t {
X86_REG_EAX = 0,
- X86_REG_ECX,
- X86_REG_EDX,
- X86_REG_EBX,
- X86_REG_ESP,
- X86_REG_EBP,
- X86_REG_ESI,
- X86_REG_EDI,
- X86_REG_EIP,
- X86_REG_EFL,
- X86_REG_CS,
- X86_REG_SS,
- X86_REG_DS,
- X86_REG_ES,
- X86_REG_FS,
- X86_REG_GS,
+ X86_REG_ECX = 1,
+ X86_REG_EDX = 2,
+ X86_REG_EBX = 3,
+ X86_REG_ESP = 4,
+ X86_REG_EBP = 5,
+ X86_REG_ESI = 6,
+ X86_REG_EDI = 7,
+ X86_REG_EIP = 8,
+ X86_REG_EFL = 9,
+ X86_REG_CS = 10,
+ X86_REG_SS = 11,
+ X86_REG_DS = 12,
+ X86_REG_ES = 13,
+ X86_REG_FS = 14,
+ X86_REG_GS = 15,
X86_REG_LAST,
X86_REG_SP = X86_REG_ESP,
X86_REG_PC = X86_REG_EIP,
};
+// Matches the numbers for the registers as generated by compilers.
+// If this is changed, then unwinding will fail.
enum X86_64Reg : uint16_t {
X86_64_REG_RAX = 0,
- X86_64_REG_RDX,
- X86_64_REG_RCX,
- X86_64_REG_RBX,
- X86_64_REG_RSI,
- X86_64_REG_RDI,
- X86_64_REG_RBP,
- X86_64_REG_RSP,
- X86_64_REG_R8,
- X86_64_REG_R9,
- X86_64_REG_R10,
- X86_64_REG_R11,
- X86_64_REG_R12,
- X86_64_REG_R13,
- X86_64_REG_R14,
- X86_64_REG_R15,
- X86_64_REG_RIP,
+ X86_64_REG_RDX = 1,
+ X86_64_REG_RCX = 2,
+ X86_64_REG_RBX = 3,
+ X86_64_REG_RSI = 4,
+ X86_64_REG_RDI = 5,
+ X86_64_REG_RBP = 6,
+ X86_64_REG_RSP = 7,
+ X86_64_REG_R8 = 8,
+ X86_64_REG_R9 = 9,
+ X86_64_REG_R10 = 10,
+ X86_64_REG_R11 = 11,
+ X86_64_REG_R12 = 12,
+ X86_64_REG_R13 = 13,
+ X86_64_REG_R14 = 14,
+ X86_64_REG_R15 = 15,
+ X86_64_REG_RIP = 16,
X86_64_REG_LAST,
X86_64_REG_SP = X86_64_REG_RSP,
diff --git a/libunwindstack/MapInfo.cpp b/libunwindstack/MapInfo.cpp
index 051f700..d7b483d 100644
--- a/libunwindstack/MapInfo.cpp
+++ b/libunwindstack/MapInfo.cpp
@@ -73,13 +73,15 @@
return new MemoryRange(memory, start, end);
}
-Elf* MapInfo::GetElf(pid_t pid, bool) {
+Elf* MapInfo::GetElf(pid_t pid, bool init_gnu_debugdata) {
if (elf) {
return elf;
}
elf = new Elf(CreateMemory(pid));
- elf->Init();
+ if (elf->Init() && init_gnu_debugdata) {
+ elf->InitGnuDebugdata();
+ }
// If the init fails, keep the elf around as an invalid object so we
// don't try to reinit the object.
return elf;
diff --git a/libunwindstack/Regs.cpp b/libunwindstack/Regs.cpp
index da22b07..92b6e22 100644
--- a/libunwindstack/Regs.cpp
+++ b/libunwindstack/Regs.cpp
@@ -27,6 +27,7 @@
#include "Machine.h"
#include "MapInfo.h"
#include "Regs.h"
+#include "Ucontext.h"
#include "User.h"
template <typename AddressType>
@@ -88,6 +89,11 @@
return rel_pc - 4;
}
+void RegsArm::SetFromRaw() {
+ set_pc(regs_[ARM_REG_PC]);
+ set_sp(regs_[ARM_REG_SP]);
+}
+
RegsArm64::RegsArm64()
: RegsImpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
@@ -102,6 +108,11 @@
return rel_pc - 4;
}
+void RegsArm64::SetFromRaw() {
+ set_pc(regs_[ARM64_REG_PC]);
+ set_sp(regs_[ARM64_REG_SP]);
+}
+
RegsX86::RegsX86()
: RegsImpl<uint32_t>(X86_REG_LAST, X86_REG_SP, Location(LOCATION_SP_OFFSET, -4)) {}
@@ -116,6 +127,11 @@
return rel_pc - 1;
}
+void RegsX86::SetFromRaw() {
+ set_pc(regs_[X86_REG_PC]);
+ set_sp(regs_[X86_REG_SP]);
+}
+
RegsX86_64::RegsX86_64()
: RegsImpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP, Location(LOCATION_SP_OFFSET, -8)) {}
@@ -131,15 +147,17 @@
return rel_pc - 1;
}
+void RegsX86_64::SetFromRaw() {
+ set_pc(regs_[X86_64_REG_PC]);
+ set_sp(regs_[X86_64_REG_SP]);
+}
+
static Regs* ReadArm(void* remote_data) {
arm_user_regs* user = reinterpret_cast<arm_user_regs*>(remote_data);
RegsArm* regs = new RegsArm();
memcpy(regs->RawData(), &user->regs[0], ARM_REG_LAST * sizeof(uint32_t));
-
- regs->set_pc(user->regs[ARM_REG_PC]);
- regs->set_sp(user->regs[ARM_REG_SP]);
-
+ regs->SetFromRaw();
return regs;
}
@@ -148,9 +166,10 @@
RegsArm64* regs = new RegsArm64();
memcpy(regs->RawData(), &user->regs[0], (ARM64_REG_R31 + 1) * sizeof(uint64_t));
- regs->set_pc(user->pc);
- regs->set_sp(user->sp);
-
+ uint64_t* reg_data = reinterpret_cast<uint64_t*>(regs->RawData());
+ reg_data[ARM64_REG_PC] = user->pc;
+ reg_data[ARM64_REG_SP] = user->sp;
+ regs->SetFromRaw();
return regs;
}
@@ -168,9 +187,7 @@
(*regs)[X86_REG_ESP] = user->esp;
(*regs)[X86_REG_EIP] = user->eip;
- regs->set_pc(user->eip);
- regs->set_sp(user->esp);
-
+ regs->SetFromRaw();
return regs;
}
@@ -196,9 +213,7 @@
(*regs)[X86_64_REG_RSP] = user->rsp;
(*regs)[X86_64_REG_RIP] = user->rip;
- regs->set_pc(user->rip);
- regs->set_sp(user->rsp);
-
+ regs->SetFromRaw();
return regs;
}
@@ -231,3 +246,111 @@
}
return nullptr;
}
+
+static Regs* CreateFromArmUcontext(void* ucontext) {
+ arm_ucontext_t* arm_ucontext = reinterpret_cast<arm_ucontext_t*>(ucontext);
+
+ RegsArm* regs = new RegsArm();
+ memcpy(regs->RawData(), &arm_ucontext->uc_mcontext.regs[0], ARM_REG_LAST * sizeof(uint32_t));
+ regs->SetFromRaw();
+ return regs;
+}
+
+static Regs* CreateFromArm64Ucontext(void* ucontext) {
+ arm64_ucontext_t* arm64_ucontext = reinterpret_cast<arm64_ucontext_t*>(ucontext);
+
+ RegsArm64* regs = new RegsArm64();
+ memcpy(regs->RawData(), &arm64_ucontext->uc_mcontext.regs[0], ARM64_REG_LAST * sizeof(uint64_t));
+ regs->SetFromRaw();
+ return regs;
+}
+
+static Regs* CreateFromX86Ucontext(void* ucontext) {
+ x86_ucontext_t* x86_ucontext = reinterpret_cast<x86_ucontext_t*>(ucontext);
+
+ RegsX86* regs = new RegsX86();
+ // Put the registers in the expected order.
+ (*regs)[X86_REG_GS] = x86_ucontext->uc_mcontext.gs;
+ (*regs)[X86_REG_FS] = x86_ucontext->uc_mcontext.fs;
+ (*regs)[X86_REG_ES] = x86_ucontext->uc_mcontext.es;
+ (*regs)[X86_REG_DS] = x86_ucontext->uc_mcontext.ds;
+ (*regs)[X86_REG_EDI] = x86_ucontext->uc_mcontext.edi;
+ (*regs)[X86_REG_ESI] = x86_ucontext->uc_mcontext.esi;
+ (*regs)[X86_REG_EBP] = x86_ucontext->uc_mcontext.ebp;
+ (*regs)[X86_REG_ESP] = x86_ucontext->uc_mcontext.esp;
+ (*regs)[X86_REG_EBX] = x86_ucontext->uc_mcontext.ebx;
+ (*regs)[X86_REG_EDX] = x86_ucontext->uc_mcontext.edx;
+ (*regs)[X86_REG_ECX] = x86_ucontext->uc_mcontext.ecx;
+ (*regs)[X86_REG_EAX] = x86_ucontext->uc_mcontext.eax;
+ (*regs)[X86_REG_EIP] = x86_ucontext->uc_mcontext.eip;
+ regs->SetFromRaw();
+ return regs;
+}
+
+static Regs* CreateFromX86_64Ucontext(void* ucontext) {
+ x86_64_ucontext_t* x86_64_ucontext = reinterpret_cast<x86_64_ucontext_t*>(ucontext);
+
+ RegsX86_64* regs = new RegsX86_64();
+ // Put the registers in the expected order.
+
+ // R8-R15
+ memcpy(&(*regs)[X86_64_REG_R8], &x86_64_ucontext->uc_mcontext.r8, 8 * sizeof(uint64_t));
+
+ // Rest of the registers.
+ (*regs)[X86_64_REG_RDI] = x86_64_ucontext->uc_mcontext.rdi;
+ (*regs)[X86_64_REG_RSI] = x86_64_ucontext->uc_mcontext.rsi;
+ (*regs)[X86_64_REG_RBP] = x86_64_ucontext->uc_mcontext.rbp;
+ (*regs)[X86_64_REG_RBX] = x86_64_ucontext->uc_mcontext.rbx;
+ (*regs)[X86_64_REG_RDX] = x86_64_ucontext->uc_mcontext.rdx;
+ (*regs)[X86_64_REG_RAX] = x86_64_ucontext->uc_mcontext.rax;
+ (*regs)[X86_64_REG_RCX] = x86_64_ucontext->uc_mcontext.rcx;
+ (*regs)[X86_64_REG_RSP] = x86_64_ucontext->uc_mcontext.rsp;
+ (*regs)[X86_64_REG_RIP] = x86_64_ucontext->uc_mcontext.rip;
+
+ regs->SetFromRaw();
+ return regs;
+}
+
+Regs* Regs::CreateFromUcontext(uint32_t machine_type, void* ucontext) {
+ switch (machine_type) {
+ case EM_386:
+ return CreateFromX86Ucontext(ucontext);
+ case EM_X86_64:
+ return CreateFromX86_64Ucontext(ucontext);
+ case EM_ARM:
+ return CreateFromArmUcontext(ucontext);
+ case EM_AARCH64:
+ return CreateFromArm64Ucontext(ucontext);
+ }
+ return nullptr;
+}
+
+uint32_t Regs::GetMachineType() {
+#if defined(__arm__)
+ return EM_ARM;
+#elif defined(__aarch64__)
+ return EM_AARCH64;
+#elif defined(__i386__)
+ return EM_386;
+#elif defined(__x86_64__)
+ return EM_X86_64;
+#else
+ abort();
+#endif
+}
+
+Regs* Regs::CreateFromLocal() {
+ Regs* regs;
+#if defined(__arm__)
+ regs = new RegsArm();
+#elif defined(__aarch64__)
+ regs = new RegsArm64();
+#elif defined(__i386__)
+ regs = new RegsX86();
+#elif defined(__x86_64__)
+ regs = new RegsX86_64();
+#else
+ abort();
+#endif
+ return regs;
+}
diff --git a/libunwindstack/Regs.h b/libunwindstack/Regs.h
index 8f5a721..a7567d8 100644
--- a/libunwindstack/Regs.h
+++ b/libunwindstack/Regs.h
@@ -54,10 +54,15 @@
virtual uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) = 0;
+ virtual void SetFromRaw() = 0;
+
uint16_t sp_reg() { return sp_reg_; }
uint16_t total_regs() { return total_regs_; }
+ static uint32_t GetMachineType();
static Regs* RemoteGet(pid_t pid, uint32_t* machine_type);
+ static Regs* CreateFromUcontext(uint32_t machine_type, void* ucontext);
+ static Regs* CreateFromLocal();
protected:
uint16_t total_regs_;
@@ -98,6 +103,8 @@
virtual ~RegsArm() = default;
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+ void SetFromRaw() override;
};
class RegsArm64 : public RegsImpl<uint64_t> {
@@ -106,6 +113,8 @@
virtual ~RegsArm64() = default;
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+ void SetFromRaw() override;
};
class RegsX86 : public RegsImpl<uint32_t> {
@@ -114,6 +123,8 @@
virtual ~RegsX86() = default;
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+ void SetFromRaw() override;
};
class RegsX86_64 : public RegsImpl<uint64_t> {
@@ -122,6 +133,8 @@
virtual ~RegsX86_64() = default;
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) override;
+
+ void SetFromRaw() override;
};
#endif // _LIBUNWINDSTACK_REGS_H
diff --git a/libunwindstack/RegsGetLocal.h b/libunwindstack/RegsGetLocal.h
new file mode 100644
index 0000000..8e6d04c
--- /dev/null
+++ b/libunwindstack/RegsGetLocal.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 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.
+ *
+ * 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.
+ */
+
+#ifndef _LIBUNWINDSTACK_REGS_GET_LOCAL_H
+#define _LIBUNWINDSTACK_REGS_GET_LOCAL_H
+
+#if defined(__arm__)
+
+inline void RegsGetLocal(Regs* regs) {
+ void* reg_data = regs->RawData();
+ asm volatile(
+ ".align 2\n"
+ "bx pc\n"
+ "nop\n"
+ ".code 32\n"
+ "stmia %[base], {r0-r12}\n"
+ "add %[base], #52\n"
+ "mov r1, r13\n"
+ "mov r2, r14\n"
+ "mov r3, r15\n"
+ "stmia %[base], {r1-r3}\n"
+ "orr %[base], pc, #1\n"
+ "bx %[base]\n"
+ : [base] "+r"(reg_data)
+ :
+ : "memory");
+
+ regs->SetFromRaw();
+}
+
+#elif defined(__aarch64__)
+
+inline void RegsGetLocal(Regs* regs) {
+ void* reg_data = regs->RawData();
+ asm volatile(
+ "1:\n"
+ "stp x0, x1, [%[base], #0]\n"
+ "stp x2, x3, [%[base], #16]\n"
+ "stp x4, x5, [%[base], #32]\n"
+ "stp x6, x7, [%[base], #48]\n"
+ "stp x8, x9, [%[base], #64]\n"
+ "stp x10, x11, [%[base], #80]\n"
+ "stp x12, x13, [%[base], #96]\n"
+ "stp x14, x15, [%[base], #112]\n"
+ "stp x16, x17, [%[base], #128]\n"
+ "stp x18, x19, [%[base], #144]\n"
+ "stp x20, x21, [%[base], #160]\n"
+ "stp x22, x23, [%[base], #176]\n"
+ "stp x24, x25, [%[base], #192]\n"
+ "stp x26, x27, [%[base], #208]\n"
+ "stp x28, x29, [%[base], #224]\n"
+ "str x30, [%[base], #240]\n"
+ "mov x12, sp\n"
+ "adr x13, 1b\n"
+ "stp x12, x13, [%[base], #248]\n"
+ : [base] "+r"(reg_data)
+ :
+ : "x12", "x13", "memory");
+
+ regs->SetFromRaw();
+}
+
+#elif defined(__i386__) || defined(__x86_64__)
+
+extern "C" void AsmGetRegs(void* regs);
+
+inline void RegsGetLocal(Regs* regs) {
+ AsmGetRegs(regs->RawData());
+
+ regs->SetFromRaw();
+}
+
+#endif
+
+#endif // _LIBUNWINDSTACK_REGS_GET_LOCAL_H
diff --git a/libunwindstack/Ucontext.h b/libunwindstack/Ucontext.h
new file mode 100644
index 0000000..95ba682
--- /dev/null
+++ b/libunwindstack/Ucontext.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2016 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.
+ *
+ * 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.
+ */
+
+#ifndef _LIBUNWINDSTACK_UCONTEXT_H
+#define _LIBUNWINDSTACK_UCONTEXT_H
+
+#include <stdint.h>
+
+//-------------------------------------------------------------------
+// ARM ucontext structures
+//-------------------------------------------------------------------
+struct arm_stack_t {
+ uint32_t ss_sp; // void __user*
+ int32_t ss_flags; // int
+ uint32_t ss_size; // size_t
+};
+
+struct arm_mcontext_t {
+ uint32_t trap_no; // unsigned long
+ uint32_t error_code; // unsigned long
+ uint32_t oldmask; // unsigned long
+ uint32_t regs[ARM_REG_LAST]; // unsigned long
+ uint32_t cpsr; // unsigned long
+ uint32_t fault_address; // unsigned long
+};
+
+struct arm_ucontext_t {
+ uint32_t uc_flags; // unsigned long
+ uint32_t uc_link; // struct ucontext*
+ arm_stack_t uc_stack;
+ arm_mcontext_t uc_mcontext;
+ // Nothing else is used, so don't define it.
+};
+//-------------------------------------------------------------------
+
+//-------------------------------------------------------------------
+// ARM64 ucontext structures
+//-------------------------------------------------------------------
+struct arm64_stack_t {
+ uint64_t ss_sp; // void __user*
+ int32_t ss_flags; // int
+ uint64_t ss_size; // size_t
+};
+
+struct arm64_sigset_t {
+ uint64_t sig; // unsigned long
+};
+
+struct arm64_mcontext_t {
+ uint64_t fault_address; // __u64
+ uint64_t regs[ARM64_REG_LAST]; // __u64
+ uint64_t pstate; // __u64
+ // Nothing else is used, so don't define it.
+};
+
+struct arm64_ucontext_t {
+ uint64_t uc_flags; // unsigned long
+ uint64_t uc_link; // struct ucontext*
+ arm64_stack_t uc_stack;
+ arm64_sigset_t uc_sigmask;
+ // The kernel adds extra padding after uc_sigmask to match glibc sigset_t on ARM64.
+ char __padding[128 - sizeof(arm64_sigset_t)];
+ // The full structure requires 16 byte alignment, but our partial structure
+ // doesn't, so force the alignment.
+ arm64_mcontext_t uc_mcontext __attribute__((aligned(16)));
+};
+//-------------------------------------------------------------------
+
+//-------------------------------------------------------------------
+// X86 ucontext structures
+//-------------------------------------------------------------------
+struct x86_stack_t {
+ uint32_t ss_sp; // void __user*
+ int32_t ss_flags; // int
+ uint32_t ss_size; // size_t
+};
+
+struct x86_mcontext_t {
+ uint32_t gs;
+ uint32_t fs;
+ uint32_t es;
+ uint32_t ds;
+ uint32_t edi;
+ uint32_t esi;
+ uint32_t ebp;
+ uint32_t esp;
+ uint32_t ebx;
+ uint32_t edx;
+ uint32_t ecx;
+ uint32_t eax;
+ uint32_t trapno;
+ uint32_t err;
+ uint32_t eip;
+ uint32_t cs;
+ uint32_t efl;
+ uint32_t uesp;
+ uint32_t ss;
+ // Only care about the registers, skip everything else.
+};
+
+struct x86_ucontext_t {
+ uint32_t uc_flags; // unsigned long
+ uint32_t uc_link; // struct ucontext*
+ x86_stack_t uc_stack;
+ x86_mcontext_t uc_mcontext;
+ // Nothing else is used, so don't define it.
+};
+//-------------------------------------------------------------------
+
+//-------------------------------------------------------------------
+// X86_64 ucontext structures
+//-------------------------------------------------------------------
+struct x86_64_stack_t {
+ uint64_t ss_sp; // void __user*
+ int32_t ss_flags; // int
+ uint64_t ss_size; // size_t
+};
+
+struct x86_64_mcontext_t {
+ uint64_t r8;
+ uint64_t r9;
+ uint64_t r10;
+ uint64_t r11;
+ uint64_t r12;
+ uint64_t r13;
+ uint64_t r14;
+ uint64_t r15;
+ uint64_t rdi;
+ uint64_t rsi;
+ uint64_t rbp;
+ uint64_t rbx;
+ uint64_t rdx;
+ uint64_t rax;
+ uint64_t rcx;
+ uint64_t rsp;
+ uint64_t rip;
+ uint64_t efl;
+ uint64_t csgsfs;
+ uint64_t err;
+ uint64_t trapno;
+ uint64_t oldmask;
+ uint64_t cr2;
+ // Only care about the registers, skip everything else.
+};
+
+typedef struct x86_64_ucontext {
+ uint64_t uc_flags; // unsigned long
+ uint64_t uc_link; // struct ucontext*
+ x86_64_stack_t uc_stack;
+ x86_64_mcontext_t uc_mcontext;
+ // Nothing else is used, so don't define it.
+} x86_64_ucontext_t;
+//-------------------------------------------------------------------
+
+#endif // _LIBUNWINDSTACK_UCONTEXT_H
diff --git a/libunwindstack/tests/DwarfMemoryTest.cpp b/libunwindstack/tests/DwarfMemoryTest.cpp
index 4877f36..b7df8f7 100644
--- a/libunwindstack/tests/DwarfMemoryTest.cpp
+++ b/libunwindstack/tests/DwarfMemoryTest.cpp
@@ -237,9 +237,13 @@
ASSERT_EQ(0U, value);
}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint32_t) { ReadEncodedValue_omit<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint32_t) {
+ ReadEncodedValue_omit<uint32_t>();
+}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint64_t) { ReadEncodedValue_omit<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint64_t) {
+ ReadEncodedValue_omit<uint64_t>();
+}
TEST_F(DwarfMemoryTest, ReadEncodedValue_absptr_uint32_t) {
uint64_t value = 100;
@@ -302,9 +306,13 @@
ASSERT_EQ(0xffffffffffffe100ULL, value);
}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint32_t) { ReadEncodedValue_leb128<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint32_t) {
+ ReadEncodedValue_leb128<uint32_t>();
+}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint64_t) { ReadEncodedValue_leb128<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint64_t) {
+ ReadEncodedValue_leb128<uint64_t>();
+}
template <typename AddressType>
void DwarfMemoryTest::ReadEncodedValue_data1() {
@@ -319,9 +327,13 @@
ASSERT_EQ(0xffffffffffffffe0ULL, value);
}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint32_t) { ReadEncodedValue_data1<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint32_t) {
+ ReadEncodedValue_data1<uint32_t>();
+}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint64_t) { ReadEncodedValue_data1<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint64_t) {
+ ReadEncodedValue_data1<uint64_t>();
+}
template <typename AddressType>
void DwarfMemoryTest::ReadEncodedValue_data2() {
@@ -336,9 +348,13 @@
ASSERT_EQ(0xffffffffffffe000ULL, value);
}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint32_t) { ReadEncodedValue_data2<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint32_t) {
+ ReadEncodedValue_data2<uint32_t>();
+}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint64_t) { ReadEncodedValue_data2<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint64_t) {
+ ReadEncodedValue_data2<uint64_t>();
+}
template <typename AddressType>
void DwarfMemoryTest::ReadEncodedValue_data4() {
@@ -353,9 +369,13 @@
ASSERT_EQ(0xffffffffe0000000ULL, value);
}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint32_t) { ReadEncodedValue_data4<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint32_t) {
+ ReadEncodedValue_data4<uint32_t>();
+}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint64_t) { ReadEncodedValue_data4<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint64_t) {
+ ReadEncodedValue_data4<uint64_t>();
+}
template <typename AddressType>
void DwarfMemoryTest::ReadEncodedValue_data8() {
@@ -370,9 +390,13 @@
ASSERT_EQ(0xe000000000000000ULL, value);
}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint32_t) { ReadEncodedValue_data8<uint32_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint32_t) {
+ ReadEncodedValue_data8<uint32_t>();
+}
-TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint64_t) { ReadEncodedValue_data8<uint64_t>(); }
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint64_t) {
+ ReadEncodedValue_data8<uint64_t>();
+}
template <typename AddressType>
void DwarfMemoryTest::ReadEncodedValue_non_zero_adjust() {
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 22cade2..bf0823b 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -24,6 +24,7 @@
#include "Elf.h"
+#include "ElfTestUtils.h"
#include "MemoryFake.h"
#if !defined(PT_ARM_EXIDX)
@@ -36,36 +37,16 @@
memory_ = new MemoryFake;
}
- template <typename Ehdr>
- void InitEhdr(Ehdr* ehdr) {
- memset(ehdr, 0, sizeof(Ehdr));
- memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG);
- ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
- ehdr->e_ident[EI_VERSION] = EV_CURRENT;
- ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
-
- ehdr->e_type = ET_DYN;
- ehdr->e_version = EV_CURRENT;
- }
-
- void InitElf32(uint32_t machine) {
+ void InitElf32(uint32_t machine_type) {
Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, machine_type);
- InitEhdr<Elf32_Ehdr>(&ehdr);
- ehdr.e_ident[EI_CLASS] = ELFCLASS32;
-
- ehdr.e_machine = machine;
- ehdr.e_entry = 0;
ehdr.e_phoff = 0x100;
- ehdr.e_shoff = 0;
- ehdr.e_flags = 0;
ehdr.e_ehsize = sizeof(ehdr);
ehdr.e_phentsize = sizeof(Elf32_Phdr);
ehdr.e_phnum = 1;
ehdr.e_shentsize = sizeof(Elf32_Shdr);
- ehdr.e_shnum = 0;
- ehdr.e_shstrndx = 0;
- if (machine == EM_ARM) {
+ if (machine_type == EM_ARM) {
ehdr.e_flags = 0x5000200;
ehdr.e_phnum = 2;
}
@@ -74,16 +55,13 @@
Elf32_Phdr phdr;
memset(&phdr, 0, sizeof(phdr));
phdr.p_type = PT_LOAD;
- phdr.p_offset = 0;
- phdr.p_vaddr = 0;
- phdr.p_paddr = 0;
phdr.p_filesz = 0x10000;
phdr.p_memsz = 0x10000;
phdr.p_flags = PF_R | PF_X;
phdr.p_align = 0x1000;
memory_->SetMemory(0x100, &phdr, sizeof(phdr));
- if (machine == EM_ARM) {
+ if (machine_type == EM_ARM) {
memset(&phdr, 0, sizeof(phdr));
phdr.p_type = PT_ARM_EXIDX;
phdr.p_offset = 0x30000;
@@ -97,31 +75,21 @@
}
}
- void InitElf64(uint32_t machine) {
+ void InitElf64(uint32_t machine_type) {
Elf64_Ehdr ehdr;
+ TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, machine_type);
- InitEhdr<Elf64_Ehdr>(&ehdr);
- ehdr.e_ident[EI_CLASS] = ELFCLASS64;
-
- ehdr.e_machine = machine;
- ehdr.e_entry = 0;
ehdr.e_phoff = 0x100;
- ehdr.e_shoff = 0;
ehdr.e_flags = 0x5000200;
ehdr.e_ehsize = sizeof(ehdr);
ehdr.e_phentsize = sizeof(Elf64_Phdr);
ehdr.e_phnum = 1;
ehdr.e_shentsize = sizeof(Elf64_Shdr);
- ehdr.e_shnum = 0;
- ehdr.e_shstrndx = 0;
memory_->SetMemory(0, &ehdr, sizeof(ehdr));
Elf64_Phdr phdr;
memset(&phdr, 0, sizeof(phdr));
phdr.p_type = PT_LOAD;
- phdr.p_offset = 0;
- phdr.p_vaddr = 0;
- phdr.p_paddr = 0;
phdr.p_filesz = 0x10000;
phdr.p_memsz = 0x10000;
phdr.p_flags = PF_R | PF_X;
@@ -129,12 +97,6 @@
memory_->SetMemory(0x100, &phdr, sizeof(phdr));
}
- template <typename Ehdr, typename Shdr>
- void GnuDebugdataInitFail(Ehdr* ehdr);
-
- template <typename Ehdr, typename Shdr>
- void GnuDebugdataInit(Ehdr* ehdr);
-
MemoryFake* memory_;
};
@@ -214,153 +176,64 @@
ASSERT_TRUE(elf.interface() != nullptr);
}
-template <typename Ehdr, typename Shdr>
-void ElfTest::GnuDebugdataInitFail(Ehdr* ehdr) {
+TEST_F(ElfTest, gnu_debugdata_init_fail32) {
+ TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, false,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(offset, ptr, size);
+ });
+
Elf elf(memory_);
-
- uint64_t offset = 0x2000;
-
- ehdr->e_shoff = offset;
- ehdr->e_shnum = 3;
- ehdr->e_shentsize = sizeof(Shdr);
- ehdr->e_shstrndx = 2;
- memory_->SetMemory(0, ehdr, sizeof(*ehdr));
-
- Shdr shdr;
- memset(&shdr, 0, sizeof(shdr));
- shdr.sh_type = SHT_NULL;
- memory_->SetMemory(offset, &shdr, sizeof(shdr));
- offset += ehdr->e_shentsize;
-
- memset(&shdr, 0, sizeof(shdr));
- shdr.sh_type = SHT_PROGBITS;
- shdr.sh_name = 0x100;
- shdr.sh_addr = 0x5000;
- shdr.sh_offset = 0x5000;
- shdr.sh_entsize = 0x100;
- shdr.sh_size = 0x800;
- memory_->SetMemory(offset, &shdr, sizeof(shdr));
- offset += ehdr->e_shentsize;
-
- memset(&shdr, 0, sizeof(shdr));
- shdr.sh_type = SHT_STRTAB;
- shdr.sh_name = 0x200000;
- shdr.sh_offset = 0xf000;
- shdr.sh_size = 0x1000;
- memory_->SetMemory(offset, &shdr, sizeof(shdr));
- offset += ehdr->e_shentsize;
-
- memory_->SetMemory(0xf100, ".gnu_debugdata", sizeof(".gnu_debugdata"));
-
ASSERT_TRUE(elf.Init());
ASSERT_TRUE(elf.interface() != nullptr);
ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
- EXPECT_EQ(0x5000U, elf.interface()->gnu_debugdata_offset());
- EXPECT_EQ(0x800U, elf.interface()->gnu_debugdata_size());
-
- elf.InitGnuDebugdata();
-}
-
-TEST_F(ElfTest, gnu_debugdata_init_fail32) {
- Elf32_Ehdr ehdr;
- InitEhdr<Elf32_Ehdr>(&ehdr);
- ehdr.e_ident[EI_CLASS] = ELFCLASS32;
- ehdr.e_machine = EM_ARM;
-
- GnuDebugdataInitFail<Elf32_Ehdr, Elf32_Shdr>(&ehdr);
+ EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
+ EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size());
}
TEST_F(ElfTest, gnu_debugdata_init_fail64) {
- Elf64_Ehdr ehdr;
- InitEhdr<Elf64_Ehdr>(&ehdr);
- ehdr.e_ident[EI_CLASS] = ELFCLASS64;
- ehdr.e_machine = EM_AARCH64;
+ TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, false,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(offset, ptr, size);
+ });
- GnuDebugdataInitFail<Elf64_Ehdr, Elf64_Shdr>(&ehdr);
-}
-
-template <typename Ehdr, typename Shdr>
-void ElfTest::GnuDebugdataInit(Ehdr* ehdr) {
Elf elf(memory_);
-
- uint64_t offset = 0x2000;
-
- ehdr->e_shoff = offset;
- ehdr->e_shnum = 3;
- ehdr->e_shentsize = sizeof(Shdr);
- ehdr->e_shstrndx = 2;
- memory_->SetMemory(0, ehdr, sizeof(*ehdr));
-
- Shdr shdr;
- memset(&shdr, 0, sizeof(shdr));
- shdr.sh_type = SHT_NULL;
- memory_->SetMemory(offset, &shdr, sizeof(shdr));
- offset += ehdr->e_shentsize;
-
- uint64_t gnu_offset = offset;
- offset += ehdr->e_shentsize;
-
- memset(&shdr, 0, sizeof(shdr));
- shdr.sh_type = SHT_STRTAB;
- shdr.sh_name = 0x200000;
- shdr.sh_offset = 0xf000;
- shdr.sh_size = 0x1000;
- memory_->SetMemory(offset, &shdr, sizeof(shdr));
- offset += ehdr->e_shentsize;
-
- memory_->SetMemory(0xf100, ".gnu_debugdata", sizeof(".gnu_debugdata"));
-
- // Read in the compressed elf data and put it in our fake memory.
- std::string name("tests/");
- if (sizeof(Ehdr) == sizeof(Elf32_Ehdr)) {
- name += "elf32.xz";
- } else {
- name += "elf64.xz";
- }
- int fd = TEMP_FAILURE_RETRY(open(name.c_str(), O_RDONLY));
- ASSERT_NE(-1, fd) << "Cannot open " + name;
- // Assumes the file is less than 1024 bytes.
- std::vector<uint8_t> buf(1024);
- ssize_t bytes = TEMP_FAILURE_RETRY(read(fd, buf.data(), buf.size()));
- ASSERT_GT(bytes, 0);
- // Make sure the file isn't too big.
- ASSERT_NE(static_cast<size_t>(bytes), buf.size())
- << "File " + name + " is too big, increase buffer size.";
- close(fd);
- buf.resize(bytes);
- memory_->SetMemory(0x5000, buf);
-
- memset(&shdr, 0, sizeof(shdr));
- shdr.sh_type = SHT_PROGBITS;
- shdr.sh_name = 0x100;
- shdr.sh_addr = 0x5000;
- shdr.sh_offset = 0x5000;
- shdr.sh_size = bytes;
- memory_->SetMemory(gnu_offset, &shdr, sizeof(shdr));
-
ASSERT_TRUE(elf.Init());
ASSERT_TRUE(elf.interface() != nullptr);
ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
- EXPECT_EQ(0x5000U, elf.interface()->gnu_debugdata_offset());
+ EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
+ EXPECT_EQ(0x100U, elf.interface()->gnu_debugdata_size());
+}
+
+TEST_F(ElfTest, gnu_debugdata_init32) {
+ TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(ELFCLASS32, EM_ARM, true,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(offset, ptr, size);
+ });
+
+ Elf elf(memory_);
+ ASSERT_TRUE(elf.Init());
+ ASSERT_TRUE(elf.interface() != nullptr);
+ ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+ EXPECT_EQ(0x1acU, elf.interface()->gnu_debugdata_offset());
+ EXPECT_EQ(0x8cU, elf.interface()->gnu_debugdata_size());
elf.InitGnuDebugdata();
ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
}
-TEST_F(ElfTest, gnu_debugdata_init32) {
- Elf32_Ehdr ehdr;
- InitEhdr<Elf32_Ehdr>(&ehdr);
- ehdr.e_ident[EI_CLASS] = ELFCLASS32;
- ehdr.e_machine = EM_ARM;
-
- GnuDebugdataInit<Elf32_Ehdr, Elf32_Shdr>(&ehdr);
-}
-
TEST_F(ElfTest, gnu_debugdata_init64) {
- Elf64_Ehdr ehdr;
- InitEhdr<Elf64_Ehdr>(&ehdr);
- ehdr.e_ident[EI_CLASS] = ELFCLASS64;
- ehdr.e_machine = EM_AARCH64;
+ TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(ELFCLASS64, EM_AARCH64, true,
+ [&](uint64_t offset, const void* ptr, size_t size) {
+ memory_->SetMemory(offset, ptr, size);
+ });
- GnuDebugdataInit<Elf64_Ehdr, Elf64_Shdr>(&ehdr);
+ Elf elf(memory_);
+ ASSERT_TRUE(elf.Init());
+ ASSERT_TRUE(elf.interface() != nullptr);
+ ASSERT_TRUE(elf.gnu_debugdata_interface() == nullptr);
+ EXPECT_EQ(0x200U, elf.interface()->gnu_debugdata_offset());
+ EXPECT_EQ(0x90U, elf.interface()->gnu_debugdata_size());
+
+ elf.InitGnuDebugdata();
+ ASSERT_TRUE(elf.gnu_debugdata_interface() != nullptr);
}
diff --git a/libunwindstack/tests/ElfTestUtils.cpp b/libunwindstack/tests/ElfTestUtils.cpp
new file mode 100644
index 0000000..81064dd
--- /dev/null
+++ b/libunwindstack/tests/ElfTestUtils.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "ElfTestUtils.h"
+
+template <typename Ehdr>
+void TestInitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine_type) {
+ memset(ehdr, 0, sizeof(Ehdr));
+ memcpy(&ehdr->e_ident[0], ELFMAG, SELFMAG);
+ ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
+ ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+ ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
+ ehdr->e_ident[EI_CLASS] = elf_class;
+ ehdr->e_type = ET_DYN;
+ ehdr->e_machine = machine_type;
+ ehdr->e_version = EV_CURRENT;
+ ehdr->e_ehsize = sizeof(Ehdr);
+}
+
+static std::string GetTestFileDirectory() {
+ std::string exec(testing::internal::GetArgvs()[0]);
+ auto const value = exec.find_last_of('/');
+ if (value == std::string::npos) {
+ return "tests/files/";
+ }
+ return exec.substr(0, value + 1) + "tests/files/";
+}
+
+template <typename Ehdr, typename Shdr>
+void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine, bool init_gnu_debugdata,
+ TestCopyFuncType copy_func) {
+ Ehdr ehdr;
+
+ TestInitEhdr(&ehdr, elf_class, machine);
+
+ uint64_t offset = sizeof(Ehdr);
+ ehdr.e_shoff = offset;
+ ehdr.e_shnum = 3;
+ ehdr.e_shentsize = sizeof(Shdr);
+ ehdr.e_shstrndx = 2;
+ copy_func(0, &ehdr, sizeof(ehdr));
+
+ Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_NULL;
+ copy_func(offset, &shdr, sizeof(shdr));
+ offset += ehdr.e_shentsize;
+
+ // Skip this header, it will contain the gnu_debugdata information.
+ uint64_t gnu_offset = offset;
+ offset += ehdr.e_shentsize;
+
+ uint64_t symtab_offset = sizeof(ehdr) + ehdr.e_shnum * ehdr.e_shentsize;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_name = 1;
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_offset = symtab_offset;
+ shdr.sh_size = 0x100;
+ copy_func(offset, &shdr, sizeof(shdr));
+
+ char value = '\0';
+ uint64_t symname_offset = symtab_offset;
+ copy_func(symname_offset, &value, 1);
+ symname_offset++;
+ std::string name(".shstrtab");
+ copy_func(symname_offset, name.c_str(), name.size() + 1);
+ symname_offset += name.size() + 1;
+ name = ".gnu_debugdata";
+ copy_func(symname_offset, name.c_str(), name.size() + 1);
+
+ ssize_t bytes = 0x100;
+ offset = symtab_offset + 0x100;
+ if (init_gnu_debugdata) {
+ // Read in the compressed elf data and copy it in.
+ name = GetTestFileDirectory();
+ if (elf_class == ELFCLASS32) {
+ name += "elf32.xz";
+ } else {
+ name += "elf64.xz";
+ }
+ int fd = TEMP_FAILURE_RETRY(open(name.c_str(), O_RDONLY));
+ ASSERT_NE(-1, fd) << "Cannot open " + name;
+ // Assumes the file is less than 1024 bytes.
+ std::vector<uint8_t> buf(1024);
+ bytes = TEMP_FAILURE_RETRY(read(fd, buf.data(), buf.size()));
+ ASSERT_GT(bytes, 0);
+ // Make sure the file isn't too big.
+ ASSERT_NE(static_cast<size_t>(bytes), buf.size())
+ << "File " + name + " is too big, increase buffer size.";
+ close(fd);
+ buf.resize(bytes);
+ copy_func(offset, buf.data(), buf.size());
+ }
+
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = symname_offset - symtab_offset;
+ shdr.sh_addr = offset;
+ shdr.sh_offset = offset;
+ shdr.sh_size = bytes;
+ copy_func(gnu_offset, &shdr, sizeof(shdr));
+}
+
+template void TestInitEhdr<Elf32_Ehdr>(Elf32_Ehdr*, uint32_t, uint32_t);
+template void TestInitEhdr<Elf64_Ehdr>(Elf64_Ehdr*, uint32_t, uint32_t);
+
+template void TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(uint32_t, uint32_t, bool,
+ TestCopyFuncType);
+template void TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(uint32_t, uint32_t, bool,
+ TestCopyFuncType);
diff --git a/libunwindstack/tests/ElfTestUtils.h b/libunwindstack/tests/ElfTestUtils.h
new file mode 100644
index 0000000..4fd3bbc
--- /dev/null
+++ b/libunwindstack/tests/ElfTestUtils.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
+#define _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
+
+#include <functional>
+
+typedef std::function<void(uint64_t, const void*, size_t)> TestCopyFuncType;
+
+template <typename Ehdr>
+void TestInitEhdr(Ehdr* ehdr, uint32_t elf_class, uint32_t machine_type);
+
+template <typename Ehdr, typename Shdr>
+void TestInitGnuDebugdata(uint32_t elf_class, uint32_t machine_type, bool init_gnu_debudata,
+ TestCopyFuncType copy_func);
+
+#endif // _LIBUNWINDSTACK_TESTS_ELF_TEST_UTILS_H
diff --git a/libunwindstack/tests/MapInfoTest.cpp b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
similarity index 85%
rename from libunwindstack/tests/MapInfoTest.cpp
rename to libunwindstack/tests/MapInfoCreateMemoryTest.cpp
index 6e47dc0..b82c841 100644
--- a/libunwindstack/tests/MapInfoTest.cpp
+++ b/libunwindstack/tests/MapInfoCreateMemoryTest.cpp
@@ -34,7 +34,7 @@
#include "MapInfo.h"
#include "Memory.h"
-class MapInfoTest : public ::testing::Test {
+class MapInfoCreateMemoryTest : public ::testing::Test {
protected:
static void SetUpTestCase() {
std::vector<uint8_t> buffer(1024);
@@ -58,10 +58,10 @@
static TemporaryFile elf_at_100_;
};
-TemporaryFile MapInfoTest::elf_;
-TemporaryFile MapInfoTest::elf_at_100_;
+TemporaryFile MapInfoCreateMemoryTest::elf_;
+TemporaryFile MapInfoCreateMemoryTest::elf_at_100_;
-TEST_F(MapInfoTest, end_le_start) {
+TEST_F(MapInfoCreateMemoryTest, end_le_start) {
MapInfo info{.start = 0x100, .end = 0x100, .offset = 0, .name = elf_.path};
std::unique_ptr<Memory> memory;
@@ -80,7 +80,7 @@
// Verify that if the offset is non-zero but there is no elf at the offset,
// that the full file is used.
-TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_full_file) {
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_full_file) {
MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_.path};
std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
@@ -100,7 +100,7 @@
// Verify that if the offset is non-zero and there is an elf at that
// offset, that only part of the file is used.
-TEST_F(MapInfoTest, create_memory_file_backed_non_zero_offset_partial_file) {
+TEST_F(MapInfoCreateMemoryTest, file_backed_non_zero_offset_partial_file) {
MapInfo info{.start = 0x100, .end = 0x200, .offset = 0x100, .name = elf_at_100_.path};
std::unique_ptr<Memory> memory(info.CreateMemory(getpid()));
@@ -119,7 +119,7 @@
}
// Verify that device file names will never result in Memory object creation.
-TEST_F(MapInfoTest, create_memory_check_device_maps) {
+TEST_F(MapInfoCreateMemoryTest, check_device_maps) {
// Set up some memory so that a valid local memory object would
// be returned if the file mapping fails, but the device check is incorrect.
std::vector<uint8_t> buffer(1024);
@@ -135,7 +135,7 @@
ASSERT_TRUE(memory.get() == nullptr);
}
-TEST_F(MapInfoTest, create_memory_local_memory) {
+TEST_F(MapInfoCreateMemoryTest, local_memory) {
// Set up some memory for a valid local memory object.
std::vector<uint8_t> buffer(1024);
for (size_t i = 0; i < buffer.size(); i++) {
@@ -160,7 +160,7 @@
ASSERT_FALSE(memory->Read(read_buffer.size(), read_buffer.data(), 1));
}
-TEST_F(MapInfoTest, create_memory_remote_memory) {
+TEST_F(MapInfoCreateMemoryTest, remote_memory) {
std::vector<uint8_t> buffer(1024);
memset(buffer.data(), 0xa, buffer.size());
@@ -201,20 +201,5 @@
ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
kill(pid, SIGKILL);
-}
-
-TEST_F(MapInfoTest, get_elf) {
- // Create a map to use as initialization data.
- void* map = mmap(nullptr, 1024, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- ASSERT_NE(MAP_FAILED, map);
-
- uint64_t start = reinterpret_cast<uint64_t>(map);
- MapInfo info{.start = start, .end = start + 1024, .offset = 0, .name = ""};
-
- // The map contains garbage, but this should still produce an elf object.
- std::unique_ptr<Elf> elf(info.GetElf(getpid(), false));
- ASSERT_TRUE(elf.get() != nullptr);
- ASSERT_FALSE(elf->valid());
-
- ASSERT_EQ(0, munmap(map, 1024));
+ ASSERT_EQ(pid, wait(nullptr));
}
diff --git a/libunwindstack/tests/MapInfoGetElfTest.cpp b/libunwindstack/tests/MapInfoGetElfTest.cpp
new file mode 100644
index 0000000..fc36cc6
--- /dev/null
+++ b/libunwindstack/tests/MapInfoGetElfTest.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 <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "Elf.h"
+#include "ElfTestUtils.h"
+#include "MapInfo.h"
+
+class MapInfoGetElfTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ map_ = mmap(nullptr, kMapSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ ASSERT_NE(MAP_FAILED, map_);
+
+ uint64_t start = reinterpret_cast<uint64_t>(map_);
+ info_.reset(new MapInfo{.start = start, .end = start + 1024, .offset = 0, .name = ""});
+ }
+
+ void TearDown() override { munmap(map_, kMapSize); }
+
+ const size_t kMapSize = 4096;
+
+ void* map_ = nullptr;
+ std::unique_ptr<MapInfo> info_;
+};
+
+TEST_F(MapInfoGetElfTest, invalid) {
+ // The map is empty, but this should still create an invalid elf object.
+ std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_FALSE(elf->valid());
+}
+
+TEST_F(MapInfoGetElfTest, valid32) {
+ Elf32_Ehdr ehdr;
+ TestInitEhdr<Elf32_Ehdr>(&ehdr, ELFCLASS32, EM_ARM);
+ memcpy(map_, &ehdr, sizeof(ehdr));
+
+ std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
+ EXPECT_EQ(ELFCLASS32, elf->class_type());
+}
+
+TEST_F(MapInfoGetElfTest, valid64) {
+ Elf64_Ehdr ehdr;
+ TestInitEhdr<Elf64_Ehdr>(&ehdr, ELFCLASS64, EM_AARCH64);
+ memcpy(map_, &ehdr, sizeof(ehdr));
+
+ std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
+ EXPECT_EQ(ELFCLASS64, elf->class_type());
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init32) {
+ TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(
+ ELFCLASS32, EM_ARM, false, [&](uint64_t offset, const void* ptr, size_t size) {
+ memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
+ });
+
+ std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
+ EXPECT_EQ(ELFCLASS32, elf->class_type());
+ EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr);
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_do_not_init64) {
+ TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(
+ ELFCLASS64, EM_AARCH64, false, [&](uint64_t offset, const void* ptr, size_t size) {
+ memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
+ });
+
+ std::unique_ptr<Elf> elf(info_->GetElf(getpid(), false));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
+ EXPECT_EQ(ELFCLASS64, elf->class_type());
+ EXPECT_TRUE(elf->gnu_debugdata_interface() == nullptr);
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_init32) {
+ TestInitGnuDebugdata<Elf32_Ehdr, Elf32_Shdr>(
+ ELFCLASS32, EM_ARM, true, [&](uint64_t offset, const void* ptr, size_t size) {
+ memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
+ });
+
+ std::unique_ptr<Elf> elf(info_->GetElf(getpid(), true));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_ARM), elf->machine_type());
+ EXPECT_EQ(ELFCLASS32, elf->class_type());
+ EXPECT_TRUE(elf->gnu_debugdata_interface() != nullptr);
+}
+
+TEST_F(MapInfoGetElfTest, gnu_debugdata_init64) {
+ TestInitGnuDebugdata<Elf64_Ehdr, Elf64_Shdr>(
+ ELFCLASS64, EM_AARCH64, true, [&](uint64_t offset, const void* ptr, size_t size) {
+ memcpy(&reinterpret_cast<uint8_t*>(map_)[offset], ptr, size);
+ });
+
+ std::unique_ptr<Elf> elf(info_->GetElf(getpid(), true));
+ ASSERT_TRUE(elf.get() != nullptr);
+ ASSERT_TRUE(elf->valid());
+ EXPECT_EQ(static_cast<uint32_t>(EM_AARCH64), elf->machine_type());
+ EXPECT_EQ(ELFCLASS64, elf->class_type());
+ EXPECT_TRUE(elf->gnu_debugdata_interface() != nullptr);
+}
diff --git a/libunwindstack/tests/MemoryBuffer.cpp b/libunwindstack/tests/MemoryBufferTest.cpp
similarity index 100%
rename from libunwindstack/tests/MemoryBuffer.cpp
rename to libunwindstack/tests/MemoryBufferTest.cpp
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index e48edf7..7210786 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -91,6 +91,7 @@
ASSERT_TRUE(Detach(pid));
kill(pid, SIGKILL);
+ ASSERT_EQ(pid, wait(nullptr));
}
TEST_F(MemoryRemoteTest, read_fail) {
@@ -131,6 +132,7 @@
ASSERT_TRUE(Detach(pid));
kill(pid, SIGKILL);
+ ASSERT_EQ(pid, wait(nullptr));
}
TEST_F(MemoryRemoteTest, read_overflow) {
@@ -160,4 +162,5 @@
ASSERT_TRUE(Detach(pid));
kill(pid, SIGKILL);
+ ASSERT_EQ(pid, wait(nullptr));
}
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index ff030c8..c57954c 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -30,6 +30,7 @@
uint64_t GetRelPc(Elf*, const MapInfo*) override { return 0; }
uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
+ void SetFromRaw() override {}
bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
};
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 3056373..dde42de 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -56,7 +56,8 @@
: RegsImpl<TypeParam>(total_regs, regs_sp, return_loc) {}
virtual ~RegsTestImpl() = default;
- uint64_t GetAdjustedPc(uint64_t, Elf*) { return 0; }
+ uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
+ void SetFromRaw() override {}
};
class RegsTest : public ::testing::Test {
@@ -264,3 +265,43 @@
ASSERT_EQ(0x800U, regs_x86_64.GetRelPc(&invalid_elf, &map_info));
ASSERT_EQ(0x800U, regs_x86_64.GetAdjustedPc(0x800U, &invalid_elf));
}
+
+TEST_F(RegsTest, arm_set_from_raw) {
+ RegsArm arm;
+ uint32_t* regs = reinterpret_cast<uint32_t*>(arm.RawData());
+ regs[13] = 0x100;
+ regs[15] = 0x200;
+ arm.SetFromRaw();
+ EXPECT_EQ(0x100U, arm.sp());
+ EXPECT_EQ(0x200U, arm.pc());
+}
+
+TEST_F(RegsTest, arm64_set_from_raw) {
+ RegsArm64 arm64;
+ uint64_t* regs = reinterpret_cast<uint64_t*>(arm64.RawData());
+ regs[31] = 0xb100000000ULL;
+ regs[32] = 0xc200000000ULL;
+ arm64.SetFromRaw();
+ EXPECT_EQ(0xb100000000U, arm64.sp());
+ EXPECT_EQ(0xc200000000U, arm64.pc());
+}
+
+TEST_F(RegsTest, x86_set_from_raw) {
+ RegsX86 x86;
+ uint32_t* regs = reinterpret_cast<uint32_t*>(x86.RawData());
+ regs[4] = 0x23450000;
+ regs[8] = 0xabcd0000;
+ x86.SetFromRaw();
+ EXPECT_EQ(0x23450000U, x86.sp());
+ EXPECT_EQ(0xabcd0000U, x86.pc());
+}
+
+TEST_F(RegsTest, x86_64_set_from_raw) {
+ RegsX86_64 x86_64;
+ uint64_t* regs = reinterpret_cast<uint64_t*>(x86_64.RawData());
+ regs[7] = 0x1200000000ULL;
+ regs[16] = 0x4900000000ULL;
+ x86_64.SetFromRaw();
+ EXPECT_EQ(0x1200000000U, x86_64.sp());
+ EXPECT_EQ(0x4900000000U, x86_64.pc());
+}
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
new file mode 100644
index 0000000..7497b65
--- /dev/null
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include <signal.h>
+#include <stdint.h>
+#include <sys/ptrace.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <atomic>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <thread>
+
+#include "Elf.h"
+#include "MapInfo.h"
+#include "Maps.h"
+#include "Memory.h"
+#include "Regs.h"
+#include "RegsGetLocal.h"
+
+static std::atomic_bool g_ready(false);
+static volatile bool g_ready_for_remote = false;
+static std::atomic_bool g_finish(false);
+static std::atomic_uintptr_t g_ucontext;
+
+static void Signal(int, siginfo_t*, void* sigcontext) {
+ g_ucontext = reinterpret_cast<uintptr_t>(sigcontext);
+ while (!g_finish.load()) {
+ }
+}
+
+static std::string ErrorMsg(const char** function_names, size_t index,
+ std::stringstream& unwind_stream) {
+ return std::string(
+ "Unwind completed without finding all frames\n"
+ " Looking for function: ") +
+ function_names[index] + "\n" + "Unwind data:\n" + unwind_stream.str();
+}
+
+static void VerifyUnwind(pid_t pid, Memory* memory, Maps* maps, Regs* regs) {
+ const char* function_names[] = {
+ "InnerFunction", "MiddleFunction", "OuterFunction",
+ };
+ size_t function_name_index = 0;
+
+ std::stringstream unwind_stream;
+ unwind_stream << std::hex;
+ for (size_t frame_num = 0; frame_num < 64; frame_num++) {
+ ASSERT_NE(0U, regs->pc()) << ErrorMsg(function_names, function_name_index, unwind_stream);
+ MapInfo* map_info = maps->Find(regs->pc());
+ ASSERT_TRUE(map_info != nullptr) << ErrorMsg(function_names, function_name_index, unwind_stream);
+
+ Elf* elf = map_info->GetElf(pid, true);
+ uint64_t rel_pc = regs->GetRelPc(elf, map_info);
+ uint64_t adjusted_rel_pc = rel_pc;
+ if (frame_num != 0) {
+ adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
+ }
+ unwind_stream << " PC: 0x" << regs->pc() << " Rel: 0x" << adjusted_rel_pc;
+ unwind_stream << " Map: ";
+ if (!map_info->name.empty()) {
+ unwind_stream << map_info->name;
+ } else {
+ unwind_stream << " anonymous";
+ }
+ unwind_stream << "<" << map_info->start << "-" << map_info->end << ">";
+
+ std::string name;
+ uint64_t func_offset;
+ if (elf->GetFunctionName(adjusted_rel_pc, &name, &func_offset)) {
+ if (name == function_names[function_name_index]) {
+ function_name_index++;
+ if (function_name_index == sizeof(function_names) / sizeof(const char*)) {
+ return;
+ }
+ }
+ unwind_stream << " " << name;
+ }
+ unwind_stream << "\n";
+ ASSERT_TRUE(elf->Step(rel_pc + map_info->elf_offset, regs, memory))
+ << ErrorMsg(function_names, function_name_index, unwind_stream);
+ }
+ ASSERT_TRUE(false) << ErrorMsg(function_names, function_name_index, unwind_stream);
+}
+
+// This test assumes that this code is compiled with optimizations turned
+// off. If this doesn't happen, then all of the calls will be optimized
+// away.
+extern "C" void InnerFunction(bool local) {
+ if (local) {
+ LocalMaps maps;
+ ASSERT_TRUE(maps.Parse());
+ std::unique_ptr<Regs> regs(Regs::CreateFromLocal());
+ RegsGetLocal(regs.get());
+ MemoryLocal memory;
+
+ VerifyUnwind(getpid(), &memory, &maps, regs.get());
+ } else {
+ g_ready_for_remote = true;
+ g_ready = true;
+ while (!g_finish.load()) {
+ }
+ }
+}
+
+extern "C" void MiddleFunction(bool local) {
+ InnerFunction(local);
+}
+
+extern "C" void OuterFunction(bool local) {
+ MiddleFunction(local);
+}
+
+TEST(UnwindTest, local) {
+ OuterFunction(true);
+}
+
+TEST(UnwindTest, remote) {
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ OuterFunction(false);
+ exit(0);
+ }
+ ASSERT_NE(-1, pid);
+
+ bool ready = false;
+ uint64_t addr = reinterpret_cast<uint64_t>(&g_ready_for_remote);
+ for (size_t i = 0; i < 100; i++) {
+ ASSERT_EQ(0, ptrace(PTRACE_ATTACH, pid, 0, 0));
+ for (size_t j = 0; j < 100; j++) {
+ siginfo_t si;
+ if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+ // Check to see if process is ready to be unwound.
+ MemoryRemote memory(pid);
+ // Read the remote value to see if we are ready.
+ bool value;
+ if (memory.Read(addr, &value, sizeof(value)) && value) {
+ ready = true;
+ break;
+ }
+ }
+ usleep(1000);
+ }
+ if (ready) {
+ break;
+ }
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
+ usleep(1000);
+ }
+ ASSERT_TRUE(read) << "Timed out waiting for remote process to be ready.";
+
+ RemoteMaps maps(pid);
+ ASSERT_TRUE(maps.Parse());
+ MemoryRemote memory(pid);
+ uint32_t machine_type;
+ std::unique_ptr<Regs> regs(Regs::RemoteGet(pid, &machine_type));
+ ASSERT_TRUE(regs.get() != nullptr);
+
+ VerifyUnwind(pid, &memory, &maps, regs.get());
+
+ ASSERT_EQ(0, ptrace(PTRACE_DETACH, pid, 0, 0));
+
+ kill(pid, SIGKILL);
+ ASSERT_EQ(pid, wait(nullptr));
+}
+
+TEST(UnwindTest, from_context) {
+ std::atomic_int tid(0);
+ std::thread thread([&]() {
+ tid = syscall(__NR_gettid);
+ OuterFunction(false);
+ });
+
+ struct sigaction act, oldact;
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = Signal;
+ act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+ ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
+ // Wait for the tid to get set.
+ for (size_t i = 0; i < 100; i++) {
+ if (tid.load() != 0) {
+ break;
+ }
+ usleep(1000);
+ }
+ ASSERT_NE(0, tid.load());
+ // Portable tgkill method.
+ ASSERT_EQ(0, syscall(__NR_tgkill, getpid(), tid.load(), SIGUSR1)) << "Failed because "
+ << strerror(errno);
+
+ // Wait for context data.
+ void* ucontext;
+ for (size_t i = 0; i < 200; i++) {
+ ucontext = reinterpret_cast<void*>(g_ucontext.load());
+ if (ucontext != nullptr) {
+ break;
+ }
+ usleep(1000);
+ }
+ ASSERT_TRUE(ucontext != nullptr) << "Timed out waiting for thread to respond to signal.";
+
+ LocalMaps maps;
+ ASSERT_TRUE(maps.Parse());
+ std::unique_ptr<Regs> regs(Regs::CreateFromUcontext(Regs::GetMachineType(), ucontext));
+ MemoryLocal memory;
+
+ VerifyUnwind(tid.load(), &memory, &maps, regs.get());
+
+ ASSERT_EQ(0, sigaction(SIGUSR1, &oldact, nullptr));
+
+ g_finish = true;
+ thread.join();
+}
diff --git a/libunwindstack/tests/elf32.xz b/libunwindstack/tests/files/elf32.xz
similarity index 100%
rename from libunwindstack/tests/elf32.xz
rename to libunwindstack/tests/files/elf32.xz
Binary files differ
diff --git a/libunwindstack/tests/elf64.xz b/libunwindstack/tests/files/elf64.xz
similarity index 100%
rename from libunwindstack/tests/elf64.xz
rename to libunwindstack/tests/files/elf64.xz
Binary files differ
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
new file mode 100644
index 0000000..34cd1ce
--- /dev/null
+++ b/libunwindstack/tools/unwind.cpp
@@ -0,0 +1,160 @@
+/*
+ * 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 <inttypes.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "Elf.h"
+#include "Maps.h"
+#include "Memory.h"
+#include "Regs.h"
+
+static bool Attach(pid_t pid) {
+ if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
+ return false;
+ }
+
+ // Allow at least 1 second to attach properly.
+ for (size_t i = 0; i < 1000; i++) {
+ siginfo_t si;
+ if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) {
+ return true;
+ }
+ usleep(1000);
+ }
+ printf("%d: Failed to stop.\n", pid);
+ return false;
+}
+
+static bool Detach(pid_t pid) {
+ return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
+}
+
+void DoUnwind(pid_t pid) {
+ RemoteMaps remote_maps(pid);
+ if (!remote_maps.Parse()) {
+ printf("Failed to parse map data.\n");
+ return;
+ }
+
+ uint32_t machine_type;
+ Regs* regs = Regs::RemoteGet(pid, &machine_type);
+ if (regs == nullptr) {
+ printf("Unable to get remote reg data\n");
+ return;
+ }
+
+ bool bits32 = true;
+ printf("ABI: ");
+ switch (machine_type) {
+ case EM_ARM:
+ printf("arm");
+ break;
+ case EM_386:
+ printf("x86");
+ break;
+ case EM_AARCH64:
+ printf("arm64");
+ bits32 = false;
+ break;
+ case EM_X86_64:
+ printf("x86_64");
+ bits32 = false;
+ break;
+ default:
+ printf("unknown\n");
+ return;
+ }
+ printf("\n");
+
+ MemoryRemote remote_memory(pid);
+ for (size_t frame_num = 0; frame_num < 64; frame_num++) {
+ if (regs->pc() == 0) {
+ break;
+ }
+ MapInfo* map_info = remote_maps.Find(regs->pc());
+ if (map_info == nullptr) {
+ printf("Failed to find map data for the pc\n");
+ break;
+ }
+
+ Elf* elf = map_info->GetElf(pid, true);
+
+ uint64_t rel_pc = regs->GetRelPc(elf, map_info);
+ uint64_t adjusted_rel_pc = rel_pc;
+ // Don't need to adjust the first frame pc.
+ if (frame_num != 0) {
+ adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
+ }
+
+ std::string name;
+ if (bits32) {
+ printf(" #%02zu pc %08" PRIx64, frame_num, adjusted_rel_pc);
+ } else {
+ printf(" #%02zu pc %016" PRIx64, frame_num, adjusted_rel_pc);
+ }
+ if (!map_info->name.empty()) {
+ printf(" %s", map_info->name.c_str());
+ if (map_info->elf_offset != 0) {
+ printf(" (offset 0x%" PRIx64 ")", map_info->elf_offset);
+ }
+ } else {
+ printf(" <anonymous:%" PRIx64 ">", map_info->offset);
+ }
+ uint64_t func_offset;
+ if (elf->GetFunctionName(adjusted_rel_pc, &name, &func_offset)) {
+ printf(" (%s", name.c_str());
+ if (func_offset != 0) {
+ printf("+%" PRId64, func_offset);
+ }
+ printf(")");
+ }
+ printf("\n");
+
+ if (!elf->Step(rel_pc + map_info->elf_offset, regs, &remote_memory)) {
+ break;
+ }
+ }
+}
+
+int main(int argc, char** argv) {
+ if (argc != 2) {
+ printf("Usage: unwind <PID>\n");
+ return 1;
+ }
+
+ pid_t pid = atoi(argv[1]);
+ if (!Attach(pid)) {
+ printf("Failed to attach to pid %d: %s\n", pid, strerror(errno));
+ return 1;
+ }
+
+ DoUnwind(pid);
+
+ Detach(pid);
+
+ return 0;
+}
diff --git a/libunwindstack/unwind_info.cpp b/libunwindstack/tools/unwind_info.cpp
similarity index 98%
rename from libunwindstack/unwind_info.cpp
rename to libunwindstack/tools/unwind_info.cpp
index da1522b..e889f8c 100644
--- a/libunwindstack/unwind_info.cpp
+++ b/libunwindstack/tools/unwind_info.cpp
@@ -21,8 +21,8 @@
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
-#include <sys/types.h>
#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
#include "ArmExidx.h"
@@ -115,10 +115,6 @@
printf("%s is not a regular file.\n", argv[1]);
return 1;
}
- if (S_ISDIR(st.st_mode)) {
- printf("%s is a directory.\n", argv[1]);
- return 1;
- }
// Send all log messages to stdout.
log_to_stdout(true);
diff --git a/libunwindstack/tools/unwind_symbols.cpp b/libunwindstack/tools/unwind_symbols.cpp
new file mode 100644
index 0000000..c010dfc
--- /dev/null
+++ b/libunwindstack/tools/unwind_symbols.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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 <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "Elf.h"
+#include "ElfInterface.h"
+#include "Log.h"
+
+int main(int argc, char** argv) {
+ if (argc != 2) {
+ printf("Need to pass the name of an elf file to the program.\n");
+ return 1;
+ }
+
+ struct stat st;
+ if (stat(argv[1], &st) == -1) {
+ printf("Cannot stat %s: %s\n", argv[1], strerror(errno));
+ return 1;
+ }
+ if (!S_ISREG(st.st_mode)) {
+ printf("%s is not a regular file.\n", argv[1]);
+ return 1;
+ }
+
+ // Send all log messages to stdout.
+ log_to_stdout(true);
+
+ MemoryFileAtOffset* memory = new MemoryFileAtOffset;
+ if (!memory->Init(argv[1], 0)) {
+ printf("Failed to init\n");
+ return 1;
+ }
+
+ Elf elf(memory);
+ if (!elf.Init() || !elf.valid()) {
+ printf("%s is not a valid elf file.\n", argv[1]);
+ return 1;
+ }
+
+ switch (elf.machine_type()) {
+ case EM_ARM:
+ printf("ABI: arm\n");
+ break;
+ case EM_AARCH64:
+ printf("ABI: arm64\n");
+ break;
+ case EM_386:
+ printf("ABI: x86\n");
+ break;
+ case EM_X86_64:
+ printf("ABI: x86_64\n");
+ break;
+ default:
+ printf("ABI: unknown\n");
+ return 1;
+ }
+
+ // This is a crude way to get the symbols in order.
+ std::string name;
+ uint64_t load_bias = elf.interface()->load_bias();
+ for (const auto& entry : elf.interface()->pt_loads()) {
+ uint64_t start = entry.second.offset + load_bias;
+ uint64_t end = entry.second.table_size + load_bias;
+ for (uint64_t addr = start; addr < end; addr += 4) {
+ std::string cur_name;
+ uint64_t func_offset;
+ if (elf.GetFunctionName(addr, &cur_name, &func_offset)) {
+ if (cur_name != name) {
+ printf("<0x%" PRIx64 "> Function: %s\n", addr - func_offset, cur_name.c_str());
+ }
+ name = cur_name;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 33770ba..0125eac 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -18,10 +18,12 @@
host_supported: true,
header_libs: [
+ "liblog_headers",
"libsystem_headers",
"libcutils_headers"
],
export_header_lib_headers: [
+ "liblog_headers",
"libsystem_headers",
"libcutils_headers"
],
@@ -76,6 +78,10 @@
header_libs: ["libutils_headers"],
export_header_lib_headers: ["libutils_headers"],
+ shared_libs: [
+ "liblog",
+ ],
+
arch: {
mips: {
cflags: ["-DALIGN_DOUBLE"],
@@ -97,7 +103,6 @@
"libbacktrace",
"libcutils",
"libdl",
- "liblog",
],
sanitize: {
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index 84a9a2f..333835c 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -50,6 +50,8 @@
"libbase",
"liblog",
],
+
+ export_include_dirs: ["include"],
}
cc_library {
@@ -65,7 +67,6 @@
"liblog",
"libbase",
],
- export_include_dirs: ["include"],
target: {
android: {
shared_libs: [
diff --git a/rootdir/init.rc b/rootdir/init.rc
index ebbec35..9301743 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -37,7 +37,7 @@
# app mem cgroups, used by activity manager, lmkd and zygote
mkdir /dev/memcg/apps/ 0755 system system
# cgroup for system_server and surfaceflinger
- mkdir /dev/memcg/system 0755 system system
+ mkdir /dev/memcg/system 0550 system system
start ueventd
@@ -376,6 +376,20 @@
# create basic filesystem structure
mkdir /data/misc 01771 system misc
+ mkdir /data/misc/recovery 0770 system log
+ copy /data/misc/recovery/default.prop /data/misc/recovery/default.prop.1
+ chmod 0440 /data/misc/recovery/default.prop.1
+ chown system log /data/misc/recovery/default.prop.1
+ copy /default.prop /data/misc/recovery/default.prop
+ chmod 0440 /data/misc/recovery/default.prop
+ chown system log /data/misc/recovery/default.prop
+ mkdir /data/misc/recovery/proc 0770 system log
+ copy /data/misc/recovery/proc/version /data/misc/recovery/proc/version.1
+ chmod 0440 /data/misc/recovery/proc/version.1
+ chown system log /data/misc/recovery/proc/version.1
+ copy /proc/version /data/misc/recovery/proc/version
+ chmod 0440 /data/misc/recovery/proc/version
+ chown system log /data/misc/recovery/proc/version
mkdir /data/misc/bluedroid 02770 bluetooth bluetooth
# Fix the access permissions and group ownership for 'bt_config.conf'
chmod 0660 /data/misc/bluedroid/bt_config.conf
@@ -681,12 +695,17 @@
on property:security.perf_harden=1
write /proc/sys/kernel/perf_event_paranoid 3
+# on shutdown
+# In device's init.rc, this trigger can be used to do device-specific actions
+# before shutdown. e.g disable watchdog and mask error handling
+
## Daemon processes to be run by init.
##
service ueventd /sbin/ueventd
class core
critical
seclabel u:r:ueventd:s0
+ shutdown critical
service healthd /sbin/healthd
class core
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index 6d35fed..9620d63 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -4,7 +4,6 @@
"bzip2",
"grep",
"grep_vendor",
- "gzip",
"mkshrc",
"mkshrc_vendor",
"reboot",