Merge "Move gcov output to /data/misc/trace"
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index 381ded4..5c0da47 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -557,9 +557,7 @@
}
void usb_reset(usb_handle* handle) {
- if (!handle->dead) {
- (*handle->interface)->USBDeviceReEnumerate(handle->interface, 0);
- }
+ // Unimplemented on OS X.
usb_kick(handle);
}
diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp
index 14cdb69..8c33ca5 100644
--- a/adb/daemon/usb.cpp
+++ b/adb/daemon/usb.cpp
@@ -304,33 +304,57 @@
switch (event.type) {
case FUNCTIONFS_BIND:
- CHECK(!bound) << "received FUNCTIONFS_BIND while already bound?";
- CHECK(!enabled) << "received FUNCTIONFS_BIND while already enabled?";
- bound = true;
+ if (bound) {
+ LOG(WARNING) << "received FUNCTIONFS_BIND while already bound?";
+ running = false;
+ }
+ if (enabled) {
+ LOG(WARNING) << "received FUNCTIONFS_BIND while already enabled?";
+ running = false;
+ }
+
+ bound = true;
break;
case FUNCTIONFS_ENABLE:
- CHECK(bound) << "received FUNCTIONFS_ENABLE while not bound?";
- CHECK(!enabled) << "received FUNCTIONFS_ENABLE while already enabled?";
- enabled = true;
+ if (!bound) {
+ LOG(WARNING) << "received FUNCTIONFS_ENABLE while not bound?";
+ running = false;
+ }
+ if (enabled) {
+ LOG(WARNING) << "received FUNCTIONFS_ENABLE while already enabled?";
+ running = false;
+ }
+
+ enabled = true;
StartWorker();
break;
case FUNCTIONFS_DISABLE:
- CHECK(bound) << "received FUNCTIONFS_DISABLE while not bound?";
- CHECK(enabled) << "received FUNCTIONFS_DISABLE while not enabled?";
- enabled = false;
+ if (!bound) {
+ LOG(WARNING) << "received FUNCTIONFS_DISABLE while not bound?";
+ }
+ if (!enabled) {
+ LOG(WARNING) << "received FUNCTIONFS_DISABLE while not enabled?";
+ }
+
+ enabled = false;
running = false;
break;
case FUNCTIONFS_UNBIND:
- CHECK(!enabled) << "received FUNCTIONFS_UNBIND while still enabled?";
- CHECK(bound) << "received FUNCTIONFS_UNBIND when not bound?";
- bound = false;
+ if (enabled) {
+ LOG(WARNING) << "received FUNCTIONFS_UNBIND while still enabled?";
+ }
+ if (!bound) {
+ LOG(WARNING) << "received FUNCTIONFS_UNBIND when not bound?";
+ }
+
+ bound = false;
running = false;
break;
}
diff --git a/adb/types.h b/adb/types.h
index 0090c98..cd1366d 100644
--- a/adb/types.h
+++ b/adb/types.h
@@ -216,7 +216,10 @@
// Add a nonempty block to the chain.
// The end of the chain must be a complete block (i.e. end_offset_ == 0).
void append(std::unique_ptr<const block_type> block) {
- CHECK_NE(0ULL, block->size());
+ if (block->size() == 0) {
+ return;
+ }
+
CHECK_EQ(0ULL, end_offset_);
chain_length_ += block->size();
chain_.emplace_back(std::move(block));
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 82ba0a1..437450c 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -48,7 +48,6 @@
#define ATRACE_TAG ATRACE_TAG_BIONIC
#include <utils/Trace.h>
-#include <unwindstack/DexFiles.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
@@ -567,7 +566,7 @@
// TODO: Use seccomp to lock ourselves down.
unwindstack::UnwinderFromPid unwinder(256, vm_pid);
- if (!unwinder.Init(unwindstack::Regs::CurrentArch())) {
+ if (!unwinder.Init()) {
LOG(FATAL) << "Failed to init unwinder object.";
}
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp
index bbec612..5f7ebc3 100644
--- a/debuggerd/handler/debuggerd_fallback.cpp
+++ b/debuggerd/handler/debuggerd_fallback.cpp
@@ -42,7 +42,6 @@
#include <android-base/file.h>
#include <android-base/unique_fd.h>
#include <async_safe/log.h>
-#include <unwindstack/DexFiles.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
@@ -81,12 +80,12 @@
thread.pid = getpid();
thread.tid = gettid();
thread.thread_name = get_thread_name(gettid());
- unwindstack::ArchEnum arch = unwindstack::Regs::CurrentArch();
- thread.registers.reset(unwindstack::Regs::CreateFromUcontext(arch, ucontext));
+ thread.registers.reset(
+ unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));
// TODO: Create this once and store it in a global?
unwindstack::UnwinderFromPid unwinder(kMaxFrames, getpid());
- if (unwinder.Init(arch)) {
+ if (unwinder.Init()) {
dump_backtrace_thread(output_fd, &unwinder, thread);
} else {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Unable to init unwinder.");
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 47a7a8f..4bdb9c8 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -44,7 +44,6 @@
#include <log/log.h>
#include <log/logprint.h>
#include <private/android_filesystem_config.h>
-#include <unwindstack/DexFiles.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
@@ -650,7 +649,7 @@
};
unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid);
- if (!unwinder.Init(unwindstack::Regs::CurrentArch())) {
+ if (!unwinder.Init()) {
LOG(FATAL) << "Failed to init unwinder object.";
}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 827db96..f8f7eb3 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1407,7 +1407,7 @@
int LocalImageSource::OpenFile(const std::string& name) const {
auto path = find_item_given_name(name);
- return open(path.c_str(), O_RDONLY);
+ return open(path.c_str(), O_RDONLY | O_BINARY);
}
static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe) {
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index ba6b9eb..045bb48 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1602,14 +1602,6 @@
return true;
}
-std::string fs_mgr_get_verity_device_name(const FstabEntry& entry) {
- if (entry.mount_point == "/") {
- // In AVB, the dm device name is vroot instead of system.
- return entry.fs_mgr_flags.avb ? "vroot" : "system";
- }
- return Basename(entry.mount_point);
-}
-
bool fs_mgr_is_verity_enabled(const FstabEntry& entry) {
if (!entry.fs_mgr_flags.verify && !entry.fs_mgr_flags.avb) {
return false;
@@ -1617,7 +1609,7 @@
DeviceMapper& dm = DeviceMapper::Instance();
- std::string mount_point = fs_mgr_get_verity_device_name(entry);
+ std::string mount_point = GetVerityDeviceName(entry);
if (dm.GetState(mount_point) == DmDeviceState::INVALID) {
return false;
}
@@ -1646,7 +1638,7 @@
}
DeviceMapper& dm = DeviceMapper::Instance();
- std::string device = fs_mgr_get_verity_device_name(entry);
+ std::string device = GetVerityDeviceName(entry);
std::vector<DeviceMapper::TargetInfo> table;
if (dm.GetState(device) == DmDeviceState::INVALID || !dm.GetTableInfo(device, &table)) {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 2f1e41f..f6f6f50 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -768,6 +768,17 @@
return system;
}
+std::string GetVerityDeviceName(const FstabEntry& entry) {
+ std::string base_device;
+ if (entry.mount_point == "/") {
+ // In AVB, the dm device name is vroot instead of system.
+ base_device = entry.fs_mgr_flags.avb ? "vroot" : "system";
+ } else {
+ base_device = android::base::Basename(entry.mount_point);
+ }
+ return base_device + "-verity";
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 24044d8..093d44d 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -340,6 +340,7 @@
blk_device = rentry.blk_device;
break;
}
+ // Find overlayfs mount point?
if ((mount_point == "/") && (rentry.mount_point == "/system")) {
blk_device = rentry.blk_device;
mount_point = "/system";
@@ -352,6 +353,12 @@
}
fs_mgr_set_blk_ro(blk_device, false);
+ // Find system-as-root mount point?
+ if ((mount_point == "/system") && !GetEntryForMountPoint(&mounts, mount_point) &&
+ GetEntryForMountPoint(&mounts, "/")) {
+ mount_point = "/";
+ }
+
// Now remount!
if (::mount(blk_device.c_str(), mount_point.c_str(), entry.fs_type.c_str(), MS_REMOUNT,
nullptr) == 0) {
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index e811447..88da41d 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -107,5 +107,10 @@
std::set<std::string> GetBootDevices();
+// Return the name of the dm-verity device for the given fstab entry. This does
+// not check whether the device is valid or exists; it merely returns the
+// expected name.
+std::string GetVerityDeviceName(const FstabEntry& entry);
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/libfiemap_writer/fiemap_writer.cpp b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
index 85589cc..e3803d5 100644
--- a/fs_mgr/libfiemap_writer/fiemap_writer.cpp
+++ b/fs_mgr/libfiemap_writer/fiemap_writer.cpp
@@ -407,9 +407,25 @@
#define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
#endif
+ // f2fs: export FS_NOCOW_FL flag to user
+ uint32_t flags;
+ int error = ioctl(file_fd, FS_IOC_GETFLAGS, &flags);
+ if (error < 0) {
+ if ((errno == ENOTTY) || (errno == ENOTSUP)) {
+ PLOG(ERROR) << "Failed to get flags, not supported by kernel: " << file_path;
+ } else {
+ PLOG(ERROR) << "Failed to get flags: " << file_path;
+ }
+ return false;
+ }
+ if (!(flags & FS_NOCOW_FL)) {
+ LOG(ERROR) << "It is not pinned: " << file_path;
+ return false;
+ }
+
// F2FS_IOC_GET_PIN_FILE returns the number of blocks moved.
uint32_t moved_blocks_nr;
- int error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &moved_blocks_nr);
+ error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &moved_blocks_nr);
if (error < 0) {
if ((errno == ENOTTY) || (errno == ENOTSUP)) {
PLOG(ERROR) << "Failed to get file pin status, not supported by kernel: " << file_path;
diff --git a/fs_mgr/libfs_avb/avb_util.cpp b/fs_mgr/libfs_avb/avb_util.cpp
index f4e4d4e..d9650f3 100644
--- a/fs_mgr/libfs_avb/avb_util.cpp
+++ b/fs_mgr/libfs_avb/avb_util.cpp
@@ -105,14 +105,15 @@
table.set_readonly(true);
const std::string mount_point(Basename(fstab_entry->mount_point));
+ const std::string device_name(GetVerityDeviceName(*fstab_entry));
android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();
- if (!dm.CreateDevice(mount_point, table)) {
+ if (!dm.CreateDevice(device_name, table)) {
LERROR << "Couldn't create verity device!";
return false;
}
std::string dev_path;
- if (!dm.GetDmDevicePathByName(mount_point, &dev_path)) {
+ if (!dm.GetDmDevicePathByName(device_name, &dev_path)) {
LERROR << "Couldn't get verity device path!";
return false;
}
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 56b5353..db27022 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -98,11 +98,12 @@
return WriteToImageFile(fd, input);
}
-SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
- const std::map<std::string, std::string>& images)
+ImageBuilder::ImageBuilder(const LpMetadata& metadata, uint32_t block_size,
+ const std::map<std::string, std::string>& images, bool sparsify)
: metadata_(metadata),
geometry_(metadata.geometry),
block_size_(block_size),
+ sparsify_(sparsify),
images_(images) {
uint64_t total_size = GetTotalSuperPartitionSize(metadata);
if (block_size % LP_SECTOR_SIZE != 0) {
@@ -144,11 +145,11 @@
}
}
-bool SparseBuilder::IsValid() const {
+bool ImageBuilder::IsValid() const {
return device_images_.size() == metadata_.block_devices.size();
}
-bool SparseBuilder::Export(const char* file) {
+bool ImageBuilder::Export(const char* file) {
unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
if (fd < 0) {
PERROR << "open failed: " << file;
@@ -158,8 +159,8 @@
LERROR << "Cannot export to a single image on retrofit builds.";
return false;
}
- // No gzip compression; sparseify; no checksum.
- int ret = sparse_file_write(device_images_[0].get(), fd, false, true, false);
+ // No gzip compression; no checksum.
+ int ret = sparse_file_write(device_images_[0].get(), fd, false, sparsify_, false);
if (ret != 0) {
LERROR << "sparse_file_write failed (error code " << ret << ")";
return false;
@@ -167,7 +168,7 @@
return true;
}
-bool SparseBuilder::ExportFiles(const std::string& output_dir) {
+bool ImageBuilder::ExportFiles(const std::string& output_dir) {
for (size_t i = 0; i < device_images_.size(); i++) {
std::string name = GetBlockDevicePartitionName(metadata_.block_devices[i]);
std::string file_name = "super_" + name + ".img";
@@ -179,8 +180,8 @@
PERROR << "open failed: " << file_path;
return false;
}
- // No gzip compression; sparseify; no checksum.
- int ret = sparse_file_write(device_images_[i].get(), fd, false, true, false);
+ // No gzip compression; no checksum.
+ int ret = sparse_file_write(device_images_[i].get(), fd, false, sparsify_, false);
if (ret != 0) {
LERROR << "sparse_file_write failed (error code " << ret << ")";
return false;
@@ -189,7 +190,7 @@
return true;
}
-bool SparseBuilder::AddData(sparse_file* file, const std::string& blob, uint64_t sector) {
+bool ImageBuilder::AddData(sparse_file* file, const std::string& blob, uint64_t sector) {
uint32_t block;
if (!SectorToBlock(sector, &block)) {
return false;
@@ -203,7 +204,7 @@
return true;
}
-bool SparseBuilder::SectorToBlock(uint64_t sector, uint32_t* block) {
+bool ImageBuilder::SectorToBlock(uint64_t sector, uint32_t* block) {
// The caller must ensure that the metadata has an alignment that is a
// multiple of the block size. liblp will take care of the rest, ensuring
// that all partitions are on an aligned boundary. Therefore all writes
@@ -218,11 +219,11 @@
return true;
}
-uint64_t SparseBuilder::BlockToSector(uint64_t block) const {
+uint64_t ImageBuilder::BlockToSector(uint64_t block) const {
return (block * block_size_) / LP_SECTOR_SIZE;
}
-bool SparseBuilder::Build() {
+bool ImageBuilder::Build() {
if (sparse_file_add_fill(device_images_[0].get(), 0, LP_PARTITION_RESERVED_BYTES, 0) < 0) {
LERROR << "Could not add initial sparse block for reserved zeroes";
return false;
@@ -275,8 +276,8 @@
return true;
}
-bool SparseBuilder::AddPartitionImage(const LpMetadataPartition& partition,
- const std::string& file) {
+bool ImageBuilder::AddPartitionImage(const LpMetadataPartition& partition,
+ const std::string& file) {
// Track which extent we're processing.
uint32_t extent_index = partition.first_extent_index;
@@ -371,7 +372,7 @@
return true;
}
-uint64_t SparseBuilder::ComputePartitionSize(const LpMetadataPartition& partition) const {
+uint64_t ImageBuilder::ComputePartitionSize(const LpMetadataPartition& partition) const {
uint64_t sectors = 0;
for (size_t i = 0; i < partition.num_extents; i++) {
sectors += metadata_.extents[partition.first_extent_index + i].num_sectors;
@@ -386,7 +387,7 @@
//
// Without this, it would be more difficult to find the appropriate extent for
// an output block. With this guarantee it is a linear walk.
-bool SparseBuilder::CheckExtentOrdering() {
+bool ImageBuilder::CheckExtentOrdering() {
std::vector<uint64_t> last_sectors(metadata_.block_devices.size());
for (const auto& extent : metadata_.extents) {
@@ -407,7 +408,7 @@
return true;
}
-int SparseBuilder::OpenImageFile(const std::string& file) {
+int ImageBuilder::OpenImageFile(const std::string& file) {
android::base::unique_fd source_fd = GetControlFileOrOpen(file.c_str(), O_RDONLY | O_CLOEXEC);
if (source_fd < 0) {
PERROR << "open image file failed: " << file;
@@ -437,15 +438,16 @@
return temp_fds_.back().get();
}
-bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
- const std::map<std::string, std::string>& images) {
- SparseBuilder builder(metadata, block_size, images);
+bool WriteToImageFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
+ const std::map<std::string, std::string>& images, bool sparsify) {
+ ImageBuilder builder(metadata, block_size, images, sparsify);
return builder.IsValid() && builder.Build() && builder.Export(file);
}
-bool WriteSplitSparseFiles(const std::string& output_dir, const LpMetadata& metadata,
- uint32_t block_size, const std::map<std::string, std::string>& images) {
- SparseBuilder builder(metadata, block_size, images);
+bool WriteSplitImageFiles(const std::string& output_dir, const LpMetadata& metadata,
+ uint32_t block_size, const std::map<std::string, std::string>& images,
+ bool sparsify) {
+ ImageBuilder builder(metadata, block_size, images, sparsify);
return builder.IsValid() && builder.Build() && builder.ExportFiles(output_dir);
}
diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h
index 44217a0..75060f9 100644
--- a/fs_mgr/liblp/images.h
+++ b/fs_mgr/liblp/images.h
@@ -32,13 +32,13 @@
bool WriteToImageFile(const char* file, const LpMetadata& metadata);
bool WriteToImageFile(int fd, const LpMetadata& metadata);
-// We use an object to build the sparse file since it requires that data
+// We use an object to build the image file since it requires that data
// pointers be held alive until the sparse file is destroyed. It's easier
// to do this when the data pointers are all in one place.
-class SparseBuilder {
+class ImageBuilder {
public:
- SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
- const std::map<std::string, std::string>& images);
+ ImageBuilder(const LpMetadata& metadata, uint32_t block_size,
+ const std::map<std::string, std::string>& images, bool sparsify);
bool Build();
bool Export(const char* file);
@@ -60,6 +60,7 @@
const LpMetadata& metadata_;
const LpMetadataGeometry& geometry_;
uint32_t block_size_;
+ bool sparsify_;
std::vector<SparsePtr> device_images_;
std::string all_metadata_;
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index 6348f55..5f782b0 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -72,8 +72,8 @@
// Read/Write logical partition metadata to an image file, for diagnostics or
// flashing.
-bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
- const std::map<std::string, std::string>& images);
+bool WriteToImageFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
+ const std::map<std::string, std::string>& images, bool sparsify);
bool WriteToImageFile(const char* file, const LpMetadata& metadata);
std::unique_ptr<LpMetadata> ReadFromImageFile(const std::string& image_file);
std::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes);
@@ -83,8 +83,9 @@
// is intended for retrofit devices, and will generate one sparse file per
// block device (each named super_<name>.img) and placed in the specified
// output folder.
-bool WriteSplitSparseFiles(const std::string& output_dir, const LpMetadata& metadata,
- uint32_t block_size, const std::map<std::string, std::string>& images);
+bool WriteSplitImageFiles(const std::string& output_dir, const LpMetadata& metadata,
+ uint32_t block_size, const std::map<std::string, std::string>& images,
+ bool sparsify);
// Helper to extract safe C++ strings from partition info.
std::string GetPartitionName(const LpMetadataPartition& partition);
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 9f3314d..8fc02cb 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -598,7 +598,7 @@
ASSERT_NE(exported, nullptr);
// Build the sparse file.
- SparseBuilder sparse(*exported.get(), 512, {});
+ ImageBuilder sparse(*exported.get(), 512, {}, true /* sparsify */);
ASSERT_TRUE(sparse.IsValid());
ASSERT_TRUE(sparse.Build());
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index c09dc3d..c22176b 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -1026,7 +1026,7 @@
elif [ -z "${ANDROID_HOST_OUT}" ]; then
echo "${ORANGE}[ WARNING ]${NORMAL} please run lunch, skipping"
else
- adb reboot-fastboot ||
+ adb reboot fastboot ||
die "fastbootd not supported (wrong adb in path?)"
any_wait 2m &&
inFastboot ||
@@ -1136,7 +1136,7 @@
echo "${GREEN}[ RUN ]${NORMAL} test fastboot flash to ${scratch_partition} recovery" >&2
- adb reboot-fastboot ||
+ adb reboot fastboot ||
die "Reboot into fastbootd"
img=${TMPDIR}/adb-remount-test-${$}.img
cleanup() {
diff --git a/init/Android.bp b/init/Android.bp
index 8292aa0..69ee34f 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -61,6 +61,7 @@
static_libs: [
"libseccomp_policy",
"libavb",
+ "libcgrouprc_format",
"libprotobuf-cpp-lite",
"libpropertyinfoserializer",
"libpropertyinfoparser",
@@ -82,6 +83,7 @@
"liblogwrap",
"liblp",
"libprocessgroup",
+ "libprocessgroup_setup",
"libselinux",
"libutils",
],
diff --git a/init/devices.cpp b/init/devices.cpp
index 1a77ba1..159c75e 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -21,8 +21,14 @@
#include <sys/sysmacros.h>
#include <unistd.h>
+#include <chrono>
+#include <map>
#include <memory>
+#include <string>
+#include <thread>
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@@ -37,12 +43,16 @@
#error "Do not include init.h in files used by ueventd; it will expose init's globals"
#endif
+using namespace std::chrono_literals;
+
using android::base::Basename;
using android::base::Dirname;
+using android::base::ReadFileToString;
using android::base::Readlink;
using android::base::Realpath;
using android::base::StartsWith;
using android::base::StringPrintf;
+using android::base::Trim;
namespace android {
namespace init {
@@ -101,6 +111,31 @@
return true;
}
+// Given a path that may start with a virtual dm block device, populate
+// the supplied buffer with the dm module's instantiated name.
+// If it doesn't start with a virtual block device, or there is some
+// error, return false.
+static bool FindDmDevicePartition(const std::string& path, std::string* result) {
+ result->clear();
+ if (!StartsWith(path, "/devices/virtual/block/dm-")) return false;
+ if (getpid() == 1) return false; // first_stage_init has no sepolicy needs
+
+ static std::map<std::string, std::string> cache;
+ // wait_for_file will not work, the content is also delayed ...
+ for (android::base::Timer t; t.duration() < 200ms; std::this_thread::sleep_for(10ms)) {
+ if (ReadFileToString("/sys" + path + "/dm/name", result) && !result->empty()) {
+ // Got it, set cache with result, when node arrives
+ cache[path] = *result = Trim(*result);
+ return true;
+ }
+ }
+ auto it = cache.find(path);
+ if ((it == cache.end()) || (it->second.empty())) return false;
+ // Return cached results, when node goes away
+ *result = it->second;
+ return true;
+}
+
Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid)
: name_(name), perm_(perm), uid_(uid), gid_(gid), prefix_(false), wildcard_(false) {
// Set 'prefix_' or 'wildcard_' based on the below cases:
@@ -293,6 +328,7 @@
std::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uevent) const {
std::string device;
std::string type;
+ std::string partition;
if (FindPlatformDevice(uevent.path, &device)) {
// Skip /devices/platform or /devices/ if present
@@ -310,6 +346,8 @@
type = "pci";
} else if (FindVbdDevicePrefix(uevent.path, &device)) {
type = "vbd";
+ } else if (FindDmDevicePartition(uevent.path, &partition)) {
+ return {"/dev/block/mapper/" + partition};
} else {
return {};
}
diff --git a/init/init.cpp b/init/init.cpp
index cdec41c..0f44efd 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -45,6 +45,7 @@
#include <libavb/libavb.h>
#include <libgsi/libgsi.h>
#include <processgroup/processgroup.h>
+#include <processgroup/setup.h>
#include <selinux/android.h>
#ifndef RECOVERY
@@ -358,7 +359,7 @@
// Have to create <CGROUPS_RC_DIR> using make_dir function
// for appropriate sepolicy to be set for it
make_dir(android::base::Dirname(CGROUPS_RC_PATH), 0711);
- if (!CgroupSetupCgroups()) {
+ if (!CgroupSetup()) {
return ErrnoError() << "Failed to setup cgroups";
}
diff --git a/init/property_service.cpp b/init/property_service.cpp
index fc5538c..467568c 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -557,9 +557,8 @@
uint32_t result =
HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
if (result != PROP_SUCCESS) {
- LOG(ERROR) << "Unable to set property '" << prop_name << "' to '" << prop_value
- << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
- << error;
+ LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
+ << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
}
break;
@@ -579,9 +578,8 @@
std::string error;
uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error);
if (result != PROP_SUCCESS) {
- LOG(ERROR) << "Unable to set property '" << name << "' to '" << value
- << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": "
- << error;
+ LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
+ << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
}
socket.SendUint32(result);
break;
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index f5f9b2a..ff19833 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -32,9 +32,6 @@
#include <unwindstack/Regs.h>
#include <unwindstack/RegsGetLocal.h>
-#if !defined(NO_LIBDEXFILE_SUPPORT)
-#include <unwindstack/DexFiles.h>
-#endif
#include <unwindstack/Unwinder.h>
#include "BacktraceLog.h"
@@ -50,14 +47,6 @@
regs, stack_map->process_memory());
unwinder.SetResolveNames(stack_map->ResolveNames());
stack_map->SetArch(regs->Arch());
- if (stack_map->GetJitDebug() != nullptr) {
- unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch());
- }
-#if !defined(NO_LIBDEXFILE_SUPPORT)
- if (stack_map->GetDexFiles() != nullptr) {
- unwinder.SetDexFiles(stack_map->GetDexFiles(), regs->Arch());
- }
-#endif
unwinder.Unwind(skip_names, &stack_map->GetSuffixesToIgnore());
if (error != nullptr) {
switch (unwinder.LastErrorCode()) {
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 4518891..726fdfa 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -43,13 +43,6 @@
// Create the process memory object.
process_memory_ = unwindstack::Memory::CreateProcessMemory(pid_);
- // Create a JitDebug object for getting jit unwind information.
- std::vector<std::string> search_libs_{"libart.so", "libartd.so"};
- jit_debug_.reset(new unwindstack::JitDebug(process_memory_, search_libs_));
-#if !defined(NO_LIBDEXFILE_SUPPORT)
- dex_files_.reset(new unwindstack::DexFiles(process_memory_, search_libs_));
-#endif
-
if (!stack_maps_->Parse()) {
return false;
}
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
index e19b605..9bb9709 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -27,9 +27,6 @@
#include <backtrace/Backtrace.h>
#include <backtrace/BacktraceMap.h>
-#if !defined(NO_LIBDEXFILE_SUPPORT)
-#include <unwindstack/DexFiles.h>
-#endif
#include <unwindstack/Elf.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
@@ -53,12 +50,6 @@
const std::shared_ptr<unwindstack::Memory>& process_memory() { return process_memory_; }
- unwindstack::JitDebug* GetJitDebug() { return jit_debug_.get(); }
-
-#if !defined(NO_LIBDEXFILE_SUPPORT)
- unwindstack::DexFiles* GetDexFiles() { return dex_files_.get(); }
-#endif
-
void SetArch(unwindstack::ArchEnum arch) { arch_ = arch; }
protected:
@@ -66,11 +57,6 @@
std::unique_ptr<unwindstack::Maps> stack_maps_;
std::shared_ptr<unwindstack::Memory> process_memory_;
- std::unique_ptr<unwindstack::JitDebug> jit_debug_;
-#if !defined(NO_LIBDEXFILE_SUPPORT)
- std::unique_ptr<unwindstack::DexFiles> dex_files_;
-#endif
-
unwindstack::ArchEnum arch_ = unwindstack::ARCH_UNKNOWN;
};
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index a27ecef..619bc56 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -235,6 +235,7 @@
"libbase",
"libjsoncpp",
"libprocessgroup",
+ "libcgrouprc",
]
cc_test {
@@ -249,7 +250,10 @@
name: "libcutils_test_static",
test_suites: ["device-tests"],
defaults: ["libcutils_test_default"],
- static_libs: ["libc"] + test_libraries,
+ static_libs: [
+ "libc",
+ "libcgrouprc_format",
+ ] + test_libraries,
stl: "libc++_static",
target: {
diff --git a/libcutils/android_reboot.cpp b/libcutils/android_reboot.cpp
index ce41cd3..e0def71 100644
--- a/libcutils/android_reboot.cpp
+++ b/libcutils/android_reboot.cpp
@@ -23,12 +23,12 @@
#define TAG "android_reboot"
-int android_reboot(int cmd, int /*flags*/, const char* arg) {
+int android_reboot(unsigned cmd, int /*flags*/, const char* arg) {
int ret;
const char* restart_cmd = NULL;
char* prop_value;
- switch (static_cast<unsigned>(cmd)) {
+ switch (cmd) {
case ANDROID_RB_RESTART: // deprecated
case ANDROID_RB_RESTART2:
restart_cmd = "reboot";
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index e35b91a..e67b458 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -73,6 +73,8 @@
#ifndef __ANDROID_VNDK__
using openFdType = int (*)();
+static openFdType openFd;
+
openFdType initOpenAshmemFd() {
openFdType openFd = nullptr;
void* handle = dlopen("libashmemd_client.so", RTLD_NOW);
@@ -221,7 +223,10 @@
int fd = -1;
#ifndef __ANDROID_VNDK__
- static auto openFd = initOpenAshmemFd();
+ if (!openFd) {
+ openFd = initOpenAshmemFd();
+ }
+
if (openFd) {
fd = openFd();
}
@@ -480,3 +485,11 @@
return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL)));
}
+
+void ashmem_init() {
+#ifndef __ANDROID_VNDK__
+ pthread_mutex_lock(&__ashmem_lock);
+ openFd = initOpenAshmemFd();
+ pthread_mutex_unlock(&__ashmem_lock);
+#endif //__ANDROID_VNDK__
+}
diff --git a/libcutils/ashmem-host.cpp b/libcutils/ashmem-host.cpp
index bb990d5..32446d4 100644
--- a/libcutils/ashmem-host.cpp
+++ b/libcutils/ashmem-host.cpp
@@ -82,3 +82,5 @@
return buf.st_size;
}
+
+void ashmem_init() {}
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index a3df380..6217bc8 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -91,7 +91,7 @@
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin" },
{ 00777, AID_ROOT, AID_ROOT, 0, "sdcard" },
{ 00751, AID_ROOT, AID_SDCARD_R, 0, "storage" },
- { 00755, AID_ROOT, AID_SHELL, 0, "system/bin" },
+ { 00751, AID_ROOT, AID_SHELL, 0, "system/bin" },
{ 00755, AID_ROOT, AID_ROOT, 0, "system/etc/ppp" },
{ 00755, AID_ROOT, AID_SHELL, 0, "system/vendor" },
{ 00751, AID_ROOT, AID_SHELL, 0, "system/xbin" },
diff --git a/libcutils/include/cutils/android_reboot.h b/libcutils/include/cutils/android_reboot.h
index 99030ed..cd27eef 100644
--- a/libcutils/include/cutils/android_reboot.h
+++ b/libcutils/include/cutils/android_reboot.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef __CUTILS_ANDROID_REBOOT_H__
-#define __CUTILS_ANDROID_REBOOT_H__
+#pragma once
#include <sys/cdefs.h>
@@ -36,10 +35,8 @@
/* Reboot or shutdown the system.
* This call uses ANDROID_RB_PROPERTY to request reboot to init process.
* Due to that, process calling this should have proper selinux permission
- * to write to the property. Otherwise, the call will fail.
+ * to write to the property or the call will fail.
*/
-int android_reboot(int cmd, int flags, const char *arg);
+int android_reboot(unsigned cmd, int flags, const char* arg);
__END_DECLS
-
-#endif /* __CUTILS_ANDROID_REBOOT_H__ */
diff --git a/libcutils/include/cutils/ashmem.h b/libcutils/include/cutils/ashmem.h
index d80caa6..abc5068 100644
--- a/libcutils/include/cutils/ashmem.h
+++ b/libcutils/include/cutils/ashmem.h
@@ -26,6 +26,7 @@
int ashmem_pin_region(int fd, size_t offset, size_t len);
int ashmem_unpin_region(int fd, size_t offset, size_t len);
int ashmem_get_size_region(int fd);
+void ashmem_init();
#ifdef __cplusplus
}
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 9b41ebe..da475cb 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -139,6 +139,5 @@
llndk_library {
name: "liblog",
symbol_file: "liblog.map.txt",
- unversioned: true,
export_include_dirs: ["include_vndk"],
}
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index b9f0dbf..66cb49f 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -24,6 +24,13 @@
"libnativebridge",
"libbase",
],
+ target: {
+ android: {
+ shared_libs: [
+ "libdl_android",
+ ],
+ },
+ },
required: [
"llndk.libraries.txt",
"vndksp.libraries.txt",
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index 07cbce9..78a02e5 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -31,6 +31,7 @@
},
shared_libs: [
"libbase",
+ "libcgrouprc",
"libjsoncpp",
],
// for cutils/android_filesystem_config.h
diff --git a/libprocessgroup/cgroup_map.cpp b/libprocessgroup/cgroup_map.cpp
index 1e66fa4..6cd6b6e 100644
--- a/libprocessgroup/cgroup_map.cpp
+++ b/libprocessgroup/cgroup_map.cpp
@@ -44,244 +44,42 @@
using android::base::StringPrintf;
using android::base::unique_fd;
-static constexpr const char* CGROUPS_DESC_FILE = "/etc/cgroups.json";
-static constexpr const char* CGROUPS_DESC_VENDOR_FILE = "/vendor/etc/cgroups.json";
-
static constexpr const char* CGROUP_PROCS_FILE = "/cgroup.procs";
static constexpr const char* CGROUP_TASKS_FILE = "/tasks";
static constexpr const char* CGROUP_TASKS_FILE_V2 = "/cgroup.tasks";
-static bool Mkdir(const std::string& path, mode_t mode, const std::string& uid,
- const std::string& gid) {
- if (mode == 0) {
- mode = 0755;
- }
-
- if (mkdir(path.c_str(), mode) != 0) {
- /* chmod in case the directory already exists */
- if (errno == EEXIST) {
- if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
- // /acct is a special case when the directory already exists
- // TODO: check if file mode is already what we want instead of using EROFS
- if (errno != EROFS) {
- PLOG(ERROR) << "fchmodat() failed for " << path;
- return false;
- }
- }
- } else {
- PLOG(ERROR) << "mkdir() failed for " << path;
- return false;
- }
- }
-
- if (uid.empty()) {
- return true;
- }
-
- passwd* uid_pwd = getpwnam(uid.c_str());
- if (!uid_pwd) {
- PLOG(ERROR) << "Unable to decode UID for '" << uid << "'";
- return false;
- }
-
- uid_t pw_uid = uid_pwd->pw_uid;
- gid_t gr_gid = -1;
- if (!gid.empty()) {
- group* gid_pwd = getgrnam(gid.c_str());
- if (!gid_pwd) {
- PLOG(ERROR) << "Unable to decode GID for '" << gid << "'";
- return false;
- }
- gr_gid = gid_pwd->gr_gid;
- }
-
- if (lchown(path.c_str(), pw_uid, gr_gid) < 0) {
- PLOG(ERROR) << "lchown() failed for " << path;
- return false;
- }
-
- /* chown may have cleared S_ISUID and S_ISGID, chmod again */
- if (mode & (S_ISUID | S_ISGID)) {
- if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
- PLOG(ERROR) << "fchmodat() failed for " << path;
- return false;
- }
- }
-
- return true;
+uint32_t CgroupController::version() const {
+ CHECK(HasValue());
+ return ACgroupController_getVersion(controller_);
}
-static bool ReadDescriptorsFromFile(const std::string& file_name,
- std::map<std::string, CgroupDescriptor>* descriptors) {
- std::vector<CgroupDescriptor> result;
- std::string json_doc;
-
- if (!android::base::ReadFileToString(file_name, &json_doc)) {
- PLOG(ERROR) << "Failed to read task profiles from " << file_name;
- return false;
- }
-
- Json::Reader reader;
- Json::Value root;
- if (!reader.parse(json_doc, root)) {
- LOG(ERROR) << "Failed to parse cgroups description: " << reader.getFormattedErrorMessages();
- return false;
- }
-
- if (root.isMember("Cgroups")) {
- const Json::Value& cgroups = root["Cgroups"];
- for (Json::Value::ArrayIndex i = 0; i < cgroups.size(); ++i) {
- std::string name = cgroups[i]["Controller"].asString();
- auto iter = descriptors->find(name);
- if (iter == descriptors->end()) {
- descriptors->emplace(name, CgroupDescriptor(1, name, cgroups[i]["Path"].asString(),
- std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
- cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString()));
- } else {
- iter->second = CgroupDescriptor(1, name, cgroups[i]["Path"].asString(),
- std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
- cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString());
- }
- }
- }
-
- if (root.isMember("Cgroups2")) {
- const Json::Value& cgroups2 = root["Cgroups2"];
- auto iter = descriptors->find(CGROUPV2_CONTROLLER_NAME);
- if (iter == descriptors->end()) {
- descriptors->emplace(CGROUPV2_CONTROLLER_NAME, CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
- std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
- cgroups2["UID"].asString(), cgroups2["GID"].asString()));
- } else {
- iter->second = CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
- std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
- cgroups2["UID"].asString(), cgroups2["GID"].asString());
- }
- }
-
- return true;
+const char* CgroupController::name() const {
+ CHECK(HasValue());
+ return ACgroupController_getName(controller_);
}
-static bool ReadDescriptors(std::map<std::string, CgroupDescriptor>* descriptors) {
- // load system cgroup descriptors
- if (!ReadDescriptorsFromFile(CGROUPS_DESC_FILE, descriptors)) {
- return false;
- }
-
- // load vendor cgroup descriptors if the file exists
- if (!access(CGROUPS_DESC_VENDOR_FILE, F_OK) &&
- !ReadDescriptorsFromFile(CGROUPS_DESC_VENDOR_FILE, descriptors)) {
- return false;
- }
-
- return true;
+const char* CgroupController::path() const {
+ CHECK(HasValue());
+ return ACgroupController_getPath(controller_);
}
-// To avoid issues in sdk_mac build
-#if defined(__ANDROID__)
-
-static bool SetupCgroup(const CgroupDescriptor& descriptor) {
- const CgroupController* controller = descriptor.controller();
-
- // mkdir <path> [mode] [owner] [group]
- if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
- LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
- return false;
- }
-
- int result;
- if (controller->version() == 2) {
- result = mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
- nullptr);
- } else {
- // Unfortunately historically cpuset controller was mounted using a mount command
- // different from all other controllers. This results in controller attributes not
- // to be prepended with controller name. For example this way instead of
- // /dev/cpuset/cpuset.cpus the attribute becomes /dev/cpuset/cpus which is what
- // the system currently expects.
- if (!strcmp(controller->name(), "cpuset")) {
- // mount cpuset none /dev/cpuset nodev noexec nosuid
- result = mount("none", controller->path(), controller->name(),
- MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr);
- } else {
- // mount cgroup none <path> nodev noexec nosuid <controller>
- result = mount("none", controller->path(), "cgroup", MS_NODEV | MS_NOEXEC | MS_NOSUID,
- controller->name());
- }
- }
-
- if (result < 0) {
- PLOG(ERROR) << "Failed to mount " << controller->name() << " cgroup";
- return false;
- }
-
- return true;
+bool CgroupController::HasValue() const {
+ return controller_ != nullptr;
}
-#else
+std::string CgroupController::GetTasksFilePath(const std::string& rel_path) const {
+ std::string tasks_path = path();
-// Stubs for non-Android targets.
-static bool SetupCgroup(const CgroupDescriptor&) {
- return false;
+ if (!rel_path.empty()) {
+ tasks_path += "/" + rel_path;
+ }
+ return (version() == 1) ? tasks_path + CGROUP_TASKS_FILE : tasks_path + CGROUP_TASKS_FILE_V2;
}
-#endif
-
-// WARNING: This function should be called only from SetupCgroups and only once.
-// It intentionally leaks an FD, so additional invocation will result in additional leak.
-static bool WriteRcFile(const std::map<std::string, CgroupDescriptor>& descriptors) {
- // WARNING: We are intentionally leaking the FD to keep the file open forever.
- // Let init keep the FD open to prevent file mappings from becoming invalid in
- // case the file gets deleted somehow.
- int fd = TEMP_FAILURE_RETRY(open(CGROUPS_RC_PATH, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
- S_IRUSR | S_IRGRP | S_IROTH));
- if (fd < 0) {
- PLOG(ERROR) << "open() failed for " << CGROUPS_RC_PATH;
- return false;
- }
-
- CgroupFile fl;
- fl.version_ = CgroupFile::FILE_CURR_VERSION;
- fl.controller_count_ = descriptors.size();
- int ret = TEMP_FAILURE_RETRY(write(fd, &fl, sizeof(fl)));
- if (ret < 0) {
- PLOG(ERROR) << "write() failed for " << CGROUPS_RC_PATH;
- return false;
- }
-
- for (const auto& [name, descriptor] : descriptors) {
- ret = TEMP_FAILURE_RETRY(write(fd, descriptor.controller(), sizeof(CgroupController)));
- if (ret < 0) {
- PLOG(ERROR) << "write() failed for " << CGROUPS_RC_PATH;
- return false;
- }
- }
-
- return true;
-}
-
-CgroupController::CgroupController(uint32_t version, const std::string& name,
- const std::string& path) {
- version_ = version;
- strncpy(name_, name.c_str(), sizeof(name_) - 1);
- name_[sizeof(name_) - 1] = '\0';
- strncpy(path_, path.c_str(), sizeof(path_) - 1);
- path_[sizeof(path_) - 1] = '\0';
-}
-
-std::string CgroupController::GetTasksFilePath(const std::string& path) const {
- std::string tasks_path = path_;
-
- if (!path.empty()) {
- tasks_path += "/" + path;
- }
- return (version_ == 1) ? tasks_path + CGROUP_TASKS_FILE : tasks_path + CGROUP_TASKS_FILE_V2;
-}
-
-std::string CgroupController::GetProcsFilePath(const std::string& path, uid_t uid,
+std::string CgroupController::GetProcsFilePath(const std::string& rel_path, uid_t uid,
pid_t pid) const {
- std::string proc_path(path_);
- proc_path.append("/").append(path);
+ std::string proc_path(path());
+ proc_path.append("/").append(rel_path);
proc_path = regex_replace(proc_path, std::regex("<uid>"), std::to_string(uid));
proc_path = regex_replace(proc_path, std::regex("<pid>"), std::to_string(pid));
@@ -302,7 +100,7 @@
return true;
}
- std::string cg_tag = StringPrintf(":%s:", name_);
+ std::string cg_tag = StringPrintf(":%s:", name());
size_t start_pos = content.find(cg_tag);
if (start_pos == std::string::npos) {
return false;
@@ -319,25 +117,12 @@
return true;
}
-CgroupDescriptor::CgroupDescriptor(uint32_t version, const std::string& name,
- const std::string& path, mode_t mode, const std::string& uid,
- const std::string& gid)
- : controller_(version, name, path), mode_(mode), uid_(uid), gid_(gid) {}
-
-CgroupMap::CgroupMap() : cg_file_data_(nullptr), cg_file_size_(0) {
+CgroupMap::CgroupMap() {
if (!LoadRcFile()) {
LOG(ERROR) << "CgroupMap::LoadRcFile called for [" << getpid() << "] failed";
}
}
-CgroupMap::~CgroupMap() {
- if (cg_file_data_) {
- munmap(cg_file_data_, cg_file_size_);
- cg_file_data_ = nullptr;
- cg_file_size_ = 0;
- }
-}
-
CgroupMap& CgroupMap::GetInstance() {
// Deliberately leak this object to avoid a race between destruction on
// process exit and concurrent access from another thread.
@@ -346,139 +131,46 @@
}
bool CgroupMap::LoadRcFile() {
- struct stat sb;
-
- if (cg_file_data_) {
- // Data already initialized
- return true;
+ if (!loaded_) {
+ loaded_ = (ACgroupFile_getVersion() != 0);
}
-
- unique_fd fd(TEMP_FAILURE_RETRY(open(CGROUPS_RC_PATH, O_RDONLY | O_CLOEXEC)));
- if (fd < 0) {
- PLOG(ERROR) << "open() failed for " << CGROUPS_RC_PATH;
- return false;
- }
-
- if (fstat(fd, &sb) < 0) {
- PLOG(ERROR) << "fstat() failed for " << CGROUPS_RC_PATH;
- return false;
- }
-
- size_t file_size = sb.st_size;
- if (file_size < sizeof(CgroupFile)) {
- LOG(ERROR) << "Invalid file format " << CGROUPS_RC_PATH;
- return false;
- }
-
- CgroupFile* file_data = (CgroupFile*)mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
- if (file_data == MAP_FAILED) {
- PLOG(ERROR) << "Failed to mmap " << CGROUPS_RC_PATH;
- return false;
- }
-
- if (file_data->version_ != CgroupFile::FILE_CURR_VERSION) {
- LOG(ERROR) << CGROUPS_RC_PATH << " file version mismatch";
- munmap(file_data, file_size);
- return false;
- }
-
- if (file_size != sizeof(CgroupFile) + file_data->controller_count_ * sizeof(CgroupController)) {
- LOG(ERROR) << CGROUPS_RC_PATH << " file has invalid size";
- munmap(file_data, file_size);
- return false;
- }
-
- cg_file_data_ = file_data;
- cg_file_size_ = file_size;
-
- return true;
+ return loaded_;
}
void CgroupMap::Print() const {
- if (!cg_file_data_) {
+ if (!loaded_) {
LOG(ERROR) << "CgroupMap::Print called for [" << getpid()
<< "] failed, RC file was not initialized properly";
return;
}
- LOG(INFO) << "File version = " << cg_file_data_->version_;
- LOG(INFO) << "File controller count = " << cg_file_data_->controller_count_;
+ LOG(INFO) << "File version = " << ACgroupFile_getVersion();
+ LOG(INFO) << "File controller count = " << ACgroupFile_getControllerCount();
LOG(INFO) << "Mounted cgroups:";
- CgroupController* controller = (CgroupController*)(cg_file_data_ + 1);
- for (int i = 0; i < cg_file_data_->controller_count_; i++, controller++) {
- LOG(INFO) << "\t" << controller->name() << " ver " << controller->version() << " path "
- << controller->path();
+
+ auto controller_count = ACgroupFile_getControllerCount();
+ for (uint32_t i = 0; i < controller_count; ++i) {
+ const ACgroupController* controller = ACgroupFile_getController(i);
+ LOG(INFO) << "\t" << ACgroupController_getName(controller) << " ver "
+ << ACgroupController_getVersion(controller) << " path "
+ << ACgroupController_getPath(controller);
}
}
-bool CgroupMap::SetupCgroups() {
- std::map<std::string, CgroupDescriptor> descriptors;
-
- if (getpid() != 1) {
- LOG(ERROR) << "Cgroup setup can be done only by init process";
- return false;
- }
-
- // Make sure we do this only one time. No need for std::call_once because
- // init is a single-threaded process
- if (access(CGROUPS_RC_PATH, F_OK) == 0) {
- LOG(WARNING) << "Attempt to call SetupCgroups more than once";
- return true;
- }
-
- // load cgroups.json file
- if (!ReadDescriptors(&descriptors)) {
- LOG(ERROR) << "Failed to load cgroup description file";
- return false;
- }
-
- // setup cgroups
- for (const auto& [name, descriptor] : descriptors) {
- if (!SetupCgroup(descriptor)) {
- // issue a warning and proceed with the next cgroup
- // TODO: mark the descriptor as invalid and skip it in WriteRcFile()
- LOG(WARNING) << "Failed to setup " << name << " cgroup";
- }
- }
-
- // mkdir <CGROUPS_RC_DIR> 0711 system system
- if (!Mkdir(android::base::Dirname(CGROUPS_RC_PATH), 0711, "system", "system")) {
- LOG(ERROR) << "Failed to create directory for " << CGROUPS_RC_PATH << " file";
- return false;
- }
-
- // Generate <CGROUPS_RC_FILE> file which can be directly mmapped into
- // process memory. This optimizes performance, memory usage
- // and limits infrormation shared with unprivileged processes
- // to the minimum subset of information from cgroups.json
- if (!WriteRcFile(descriptors)) {
- LOG(ERROR) << "Failed to write " << CGROUPS_RC_PATH << " file";
- return false;
- }
-
- // chmod 0644 <CGROUPS_RC_PATH>
- if (fchmodat(AT_FDCWD, CGROUPS_RC_PATH, 0644, AT_SYMLINK_NOFOLLOW) < 0) {
- PLOG(ERROR) << "fchmodat() failed";
- return false;
- }
-
- return true;
-}
-
-const CgroupController* CgroupMap::FindController(const std::string& name) const {
- if (!cg_file_data_) {
+CgroupController CgroupMap::FindController(const std::string& name) const {
+ if (!loaded_) {
LOG(ERROR) << "CgroupMap::FindController called for [" << getpid()
<< "] failed, RC file was not initialized properly";
- return nullptr;
+ return CgroupController(nullptr);
}
- // skip the file header to get to the first controller
- CgroupController* controller = (CgroupController*)(cg_file_data_ + 1);
- for (int i = 0; i < cg_file_data_->controller_count_; i++, controller++) {
- if (name == controller->name()) {
- return controller;
+ auto controller_count = ACgroupFile_getControllerCount();
+ for (uint32_t i = 0; i < controller_count; ++i) {
+ const ACgroupController* controller = ACgroupFile_getController(i);
+ if (name == ACgroupController_getName(controller)) {
+ return CgroupController(controller);
}
}
- return nullptr;
+ return CgroupController(nullptr);
}
diff --git a/libprocessgroup/cgroup_map.h b/libprocessgroup/cgroup_map.h
index 304ae15..d765e60 100644
--- a/libprocessgroup/cgroup_map.h
+++ b/libprocessgroup/cgroup_map.h
@@ -20,57 +20,30 @@
#include <sys/types.h>
#include <map>
+#include <memory>
#include <mutex>
#include <string>
+#include <vector>
-// Minimal controller description to be mmapped into process address space
+#include <android/cgrouprc.h>
+
+// Convenient wrapper of an ACgroupController pointer.
class CgroupController {
public:
- CgroupController() {}
- CgroupController(uint32_t version, const std::string& name, const std::string& path);
+ // Does not own controller
+ explicit CgroupController(const ACgroupController* controller) : controller_(controller) {}
- uint32_t version() const { return version_; }
- const char* name() const { return name_; }
- const char* path() const { return path_; }
+ uint32_t version() const;
+ const char* name() const;
+ const char* path() const;
+
+ bool HasValue() const;
std::string GetTasksFilePath(const std::string& path) const;
std::string GetProcsFilePath(const std::string& path, uid_t uid, pid_t pid) const;
bool GetTaskGroup(int tid, std::string* group) const;
-
private:
- static constexpr size_t CGROUP_NAME_BUF_SZ = 16;
- static constexpr size_t CGROUP_PATH_BUF_SZ = 32;
-
- uint32_t version_;
- char name_[CGROUP_NAME_BUF_SZ];
- char path_[CGROUP_PATH_BUF_SZ];
-};
-
-// Complete controller description for mounting cgroups
-class CgroupDescriptor {
- public:
- CgroupDescriptor(uint32_t version, const std::string& name, const std::string& path,
- mode_t mode, const std::string& uid, const std::string& gid);
-
- const CgroupController* controller() const { return &controller_; }
- mode_t mode() const { return mode_; }
- std::string uid() const { return uid_; }
- std::string gid() const { return gid_; }
-
- private:
- CgroupController controller_;
- mode_t mode_;
- std::string uid_;
- std::string gid_;
-};
-
-struct CgroupFile {
- static constexpr uint32_t FILE_VERSION_1 = 1;
- static constexpr uint32_t FILE_CURR_VERSION = FILE_VERSION_1;
-
- uint32_t version_;
- uint32_t controller_count_;
- CgroupController controllers_[];
+ const ACgroupController* controller_ = nullptr;
};
class CgroupMap {
@@ -79,16 +52,11 @@
static bool SetupCgroups();
static CgroupMap& GetInstance();
-
- const CgroupController* FindController(const std::string& name) const;
+ CgroupController FindController(const std::string& name) const;
private:
- struct CgroupFile* cg_file_data_;
- size_t cg_file_size_;
-
+ bool loaded_ = false;
CgroupMap();
- ~CgroupMap();
-
bool LoadRcFile();
void Print() const;
};
diff --git a/libprocessgroup/cgrouprc/Android.bp b/libprocessgroup/cgrouprc/Android.bp
new file mode 100644
index 0000000..6848620
--- /dev/null
+++ b/libprocessgroup/cgrouprc/Android.bp
@@ -0,0 +1,61 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library {
+ name: "libcgrouprc",
+ host_supported: true,
+ recovery_available: true,
+ // Do not ever mark this as vendor_available; otherwise, vendor modules
+ // that links to the static library will behave unexpectedly. All on-device
+ // modules should use libprocessgroup which links to the LL-NDK library
+ // defined below. The static library is built for tests.
+ vendor_available: false,
+ srcs: [
+ "cgroup_controller.cpp",
+ "cgroup_file.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+ header_libs: [
+ "libprocessgroup_headers",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+ static_libs: [
+ "libcgrouprc_format",
+ ],
+ stubs: {
+ symbol_file: "libcgrouprc.map.txt",
+ versions: ["29"],
+ },
+ target: {
+ linux: {
+ version_script: "libcgrouprc.map.txt",
+ },
+ },
+}
+
+llndk_library {
+ name: "libcgrouprc",
+ symbol_file: "libcgrouprc.map.txt",
+ export_include_dirs: [
+ "include",
+ ],
+}
diff --git a/libprocessgroup/cgrouprc/cgroup_controller.cpp b/libprocessgroup/cgrouprc/cgroup_controller.cpp
new file mode 100644
index 0000000..d064d31
--- /dev/null
+++ b/libprocessgroup/cgrouprc/cgroup_controller.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android/cgrouprc.h>
+
+#include "cgrouprc_internal.h"
+
+// All ACgroupController_* functions implicitly convert the pointer back
+// to the original CgroupController pointer before invoking the member functions.
+
+uint32_t ACgroupController_getVersion(const ACgroupController* controller) {
+ CHECK(controller != nullptr);
+ return controller->version();
+}
+
+const char* ACgroupController_getName(const ACgroupController* controller) {
+ CHECK(controller != nullptr);
+ return controller->name();
+}
+
+const char* ACgroupController_getPath(const ACgroupController* controller) {
+ CHECK(controller != nullptr);
+ return controller->path();
+}
diff --git a/libprocessgroup/cgrouprc/cgroup_file.cpp b/libprocessgroup/cgrouprc/cgroup_file.cpp
new file mode 100644
index 0000000..e26d841
--- /dev/null
+++ b/libprocessgroup/cgrouprc/cgroup_file.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 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/mman.h>
+#include <sys/stat.h>
+
+#include <memory>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <android/cgrouprc.h>
+#include <processgroup/processgroup.h>
+
+#include "cgrouprc_internal.h"
+
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+using android::cgrouprc::format::CgroupController;
+using android::cgrouprc::format::CgroupFile;
+
+static CgroupFile* LoadRcFile() {
+ struct stat sb;
+
+ unique_fd fd(TEMP_FAILURE_RETRY(open(CGROUPS_RC_PATH, O_RDONLY | O_CLOEXEC)));
+ if (fd < 0) {
+ PLOG(ERROR) << "open() failed for " << CGROUPS_RC_PATH;
+ return nullptr;
+ }
+
+ if (fstat(fd, &sb) < 0) {
+ PLOG(ERROR) << "fstat() failed for " << CGROUPS_RC_PATH;
+ return nullptr;
+ }
+
+ size_t file_size = sb.st_size;
+ if (file_size < sizeof(CgroupFile)) {
+ LOG(ERROR) << "Invalid file format " << CGROUPS_RC_PATH;
+ return nullptr;
+ }
+
+ CgroupFile* file_data = (CgroupFile*)mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
+ if (file_data == MAP_FAILED) {
+ PLOG(ERROR) << "Failed to mmap " << CGROUPS_RC_PATH;
+ return nullptr;
+ }
+
+ if (file_data->version_ != CgroupFile::FILE_CURR_VERSION) {
+ LOG(ERROR) << CGROUPS_RC_PATH << " file version mismatch";
+ munmap(file_data, file_size);
+ return nullptr;
+ }
+
+ auto expected = sizeof(CgroupFile) + file_data->controller_count_ * sizeof(CgroupController);
+ if (file_size != expected) {
+ LOG(ERROR) << CGROUPS_RC_PATH << " file has invalid size, expected " << expected
+ << ", actual " << file_size;
+ munmap(file_data, file_size);
+ return nullptr;
+ }
+
+ return file_data;
+}
+
+static CgroupFile* GetInstance() {
+ // Deliberately leak this object (not munmap) to avoid a race between destruction on
+ // process exit and concurrent access from another thread.
+ static auto* file = LoadRcFile();
+ return file;
+}
+
+uint32_t ACgroupFile_getVersion() {
+ auto file = GetInstance();
+ if (file == nullptr) return 0;
+ return file->version_;
+}
+
+uint32_t ACgroupFile_getControllerCount() {
+ auto file = GetInstance();
+ if (file == nullptr) return 0;
+ return file->controller_count_;
+}
+
+const ACgroupController* ACgroupFile_getController(uint32_t index) {
+ auto file = GetInstance();
+ if (file == nullptr) return nullptr;
+ CHECK(index < file->controller_count_);
+ // Although the object is not actually an ACgroupController object, all ACgroupController_*
+ // functions implicitly convert ACgroupController* back to CgroupController* before invoking
+ // member functions.
+ return static_cast<ACgroupController*>(&file->controllers_[index]);
+}
diff --git a/libprocessgroup/cgrouprc/cgrouprc_internal.h b/libprocessgroup/cgrouprc/cgrouprc_internal.h
new file mode 100644
index 0000000..cd02f03
--- /dev/null
+++ b/libprocessgroup/cgrouprc/cgrouprc_internal.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <android/cgrouprc.h>
+
+#include <processgroup/format/cgroup_controller.h>
+#include <processgroup/format/cgroup_file.h>
+
+struct ACgroupController : android::cgrouprc::format::CgroupController {};
diff --git a/libprocessgroup/cgrouprc/include/android/cgrouprc.h b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
new file mode 100644
index 0000000..4edd239
--- /dev/null
+++ b/libprocessgroup/cgrouprc/include/android/cgrouprc.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+__BEGIN_DECLS
+
+// For host builds, __INTRODUCED_IN is not defined.
+#ifndef __INTRODUCED_IN
+#define __INTRODUCED_IN(x)
+#endif
+
+struct ACgroupController;
+typedef struct ACgroupController ACgroupController;
+
+#if __ANDROID_API__ >= __ANDROID_API_Q__
+
+// ACgroupFile
+
+/**
+ * Returns file version. See android::cgrouprc::format::CgroupFile for a list of valid versions
+ * for the file.
+ * If ACgroupFile_init() isn't called, initialization will be done first.
+ * If initialization failed, return 0.
+ */
+__attribute__((warn_unused_result)) uint32_t ACgroupFile_getVersion() __INTRODUCED_IN(29);
+
+/**
+ * Returns the number of controllers.
+ * If ACgroupFile_init() isn't called, initialization will be done first.
+ * If initialization failed, return 0.
+ */
+__attribute__((warn_unused_result)) uint32_t ACgroupFile_getControllerCount() __INTRODUCED_IN(29);
+
+/**
+ * Returns the controller at the given index.
+ * Returnss nullptr if the given index exceeds getControllerCount().
+ * If ACgroupFile_init() isn't called, initialization will be done first.
+ * If initialization failed, return 0.
+ */
+__attribute__((warn_unused_result)) const ACgroupController* ACgroupFile_getController(
+ uint32_t index) __INTRODUCED_IN(29);
+
+// ACgroupController
+
+/**
+ * Returns the version of the given controller.
+ * If the given controller is null, return 0.
+ */
+__attribute__((warn_unused_result)) uint32_t ACgroupController_getVersion(const ACgroupController*)
+ __INTRODUCED_IN(29);
+
+/**
+ * Returns the name of the given controller.
+ * If the given controller is null, return nullptr.
+ */
+__attribute__((warn_unused_result)) const char* ACgroupController_getName(const ACgroupController*)
+ __INTRODUCED_IN(29);
+
+/**
+ * Returns the path of the given controller.
+ * If the given controller is null, return nullptr.
+ */
+__attribute__((warn_unused_result)) const char* ACgroupController_getPath(const ACgroupController*)
+ __INTRODUCED_IN(29);
+
+__END_DECLS
+
+#endif
diff --git a/libprocessgroup/cgrouprc/libcgrouprc.map.txt b/libprocessgroup/cgrouprc/libcgrouprc.map.txt
new file mode 100644
index 0000000..91df392
--- /dev/null
+++ b/libprocessgroup/cgrouprc/libcgrouprc.map.txt
@@ -0,0 +1,11 @@
+LIBCGROUPRC { # introduced=29
+ global:
+ ACgroupFile_getVersion;
+ ACgroupFile_getControllerCount;
+ ACgroupFile_getController;
+ ACgroupController_getVersion;
+ ACgroupController_getName;
+ ACgroupController_getPath;
+ local:
+ *;
+};
diff --git a/libprocessgroup/cgrouprc_format/Android.bp b/libprocessgroup/cgrouprc_format/Android.bp
new file mode 100644
index 0000000..dfbeed7
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_static {
+ name: "libcgrouprc_format",
+ host_supported: true,
+ recovery_available: true,
+ srcs: [
+ "cgroup_controller.cpp",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+}
diff --git a/libprocessgroup/cgrouprc_format/cgroup_controller.cpp b/libprocessgroup/cgrouprc_format/cgroup_controller.cpp
new file mode 100644
index 0000000..877eed8
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/cgroup_controller.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 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 <processgroup/format/cgroup_controller.h>
+
+namespace android {
+namespace cgrouprc {
+namespace format {
+
+CgroupController::CgroupController(uint32_t version, const std::string& name,
+ const std::string& path) {
+ // strlcpy isn't available on host. Although there is an implementation
+ // in licutils, libcutils itself depends on libcgrouprc_format, causing
+ // a circular dependency.
+ version_ = version;
+ strncpy(name_, name.c_str(), sizeof(name_) - 1);
+ name_[sizeof(name_) - 1] = '\0';
+ strncpy(path_, path.c_str(), sizeof(path_) - 1);
+ path_[sizeof(path_) - 1] = '\0';
+}
+
+uint32_t CgroupController::version() const {
+ return version_;
+}
+
+const char* CgroupController::name() const {
+ return name_;
+}
+
+const char* CgroupController::path() const {
+ return path_;
+}
+
+} // namespace format
+} // namespace cgrouprc
+} // namespace android
diff --git a/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h
new file mode 100644
index 0000000..64c7532
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_controller.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <string>
+
+namespace android {
+namespace cgrouprc {
+namespace format {
+
+// Minimal controller description to be mmapped into process address space
+struct CgroupController {
+ public:
+ CgroupController() {}
+ CgroupController(uint32_t version, const std::string& name, const std::string& path);
+
+ uint32_t version() const;
+ const char* name() const;
+ const char* path() const;
+
+ private:
+ static constexpr size_t CGROUP_NAME_BUF_SZ = 16;
+ static constexpr size_t CGROUP_PATH_BUF_SZ = 32;
+
+ uint32_t version_;
+ char name_[CGROUP_NAME_BUF_SZ];
+ char path_[CGROUP_PATH_BUF_SZ];
+};
+
+} // namespace format
+} // namespace cgrouprc
+} // namespace android
diff --git a/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h
new file mode 100644
index 0000000..f1678a1
--- /dev/null
+++ b/libprocessgroup/cgrouprc_format/include/processgroup/format/cgroup_file.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <processgroup/format/cgroup_controller.h>
+
+namespace android {
+namespace cgrouprc {
+namespace format {
+
+struct CgroupFile {
+ uint32_t version_;
+ uint32_t controller_count_;
+ CgroupController controllers_[];
+
+ static constexpr uint32_t FILE_VERSION_1 = 1;
+ static constexpr uint32_t FILE_CURR_VERSION = FILE_VERSION_1;
+};
+
+} // namespace format
+} // namespace cgrouprc
+} // namespace android
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 46b8676..86e6035 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -26,7 +26,6 @@
static constexpr const char* CGROUPV2_CONTROLLER_NAME = "cgroup2";
static constexpr const char* CGROUPS_RC_PATH = "/dev/cgroup_info/cgroup.rc";
-bool CgroupSetupCgroups();
bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path);
bool CgroupGetAttributePath(const std::string& attr_name, std::string* path);
bool CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path);
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 8884650..abe63dd 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -55,19 +55,15 @@
#define PROCESSGROUP_CGROUP_PROCS_FILE "/cgroup.procs"
-bool CgroupSetupCgroups() {
- return CgroupMap::SetupCgroups();
-}
-
bool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path) {
- const CgroupController* controller = CgroupMap::GetInstance().FindController(cgroup_name);
+ auto controller = CgroupMap::GetInstance().FindController(cgroup_name);
- if (controller == nullptr) {
+ if (!controller.HasValue()) {
return false;
}
if (path) {
- *path = controller->path();
+ *path = controller.path();
}
return true;
@@ -111,7 +107,7 @@
static bool isMemoryCgroupSupported() {
std::string cgroup_name;
- static bool memcg_supported = (CgroupMap::GetInstance().FindController("memory") != nullptr);
+ static bool memcg_supported = CgroupMap::GetInstance().FindController("memory").HasValue();
return memcg_supported;
}
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index 1eefada..c7d0cca 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -152,21 +152,21 @@
}
bool cpusets_enabled() {
- static bool enabled = (CgroupMap::GetInstance().FindController("cpuset") != nullptr);
+ static bool enabled = (CgroupMap::GetInstance().FindController("cpuset").HasValue());
return enabled;
}
bool schedboost_enabled() {
- static bool enabled = (CgroupMap::GetInstance().FindController("schedtune") != nullptr);
+ static bool enabled = (CgroupMap::GetInstance().FindController("schedtune").HasValue());
return enabled;
}
static int getCGroupSubsys(int tid, const char* subsys, std::string& subgroup) {
- const CgroupController* controller = CgroupMap::GetInstance().FindController(subsys);
+ auto controller = CgroupMap::GetInstance().FindController(subsys);
- if (!controller) return -1;
+ if (!controller.HasValue()) return -1;
- if (!controller->GetTaskGroup(tid, &subgroup)) {
+ if (!controller.GetTaskGroup(tid, &subgroup)) {
LOG(ERROR) << "Failed to find cgroup for tid " << tid;
return -1;
}
diff --git a/libprocessgroup/setup/Android.bp b/libprocessgroup/setup/Android.bp
new file mode 100644
index 0000000..f6fc066
--- /dev/null
+++ b/libprocessgroup/setup/Android.bp
@@ -0,0 +1,44 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_library_shared {
+ name: "libprocessgroup_setup",
+ recovery_available: true,
+ srcs: [
+ "cgroup_map_write.cpp",
+ ],
+ export_include_dirs: [
+ "include",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcgrouprc",
+ "libjsoncpp",
+ ],
+ static_libs: [
+ "libcgrouprc_format",
+ ],
+ header_libs: [
+ "libprocessgroup_headers",
+ ],
+ export_header_lib_headers: [
+ "libprocessgroup_headers",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
diff --git a/libprocessgroup/setup/cgroup_descriptor.h b/libprocessgroup/setup/cgroup_descriptor.h
new file mode 100644
index 0000000..597060e
--- /dev/null
+++ b/libprocessgroup/setup/cgroup_descriptor.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <processgroup/format/cgroup_controller.h>
+
+namespace android {
+namespace cgrouprc {
+
+// Complete controller description for mounting cgroups
+class CgroupDescriptor {
+ public:
+ CgroupDescriptor(uint32_t version, const std::string& name, const std::string& path,
+ mode_t mode, const std::string& uid, const std::string& gid);
+
+ const format::CgroupController* controller() const { return &controller_; }
+ mode_t mode() const { return mode_; }
+ std::string uid() const { return uid_; }
+ std::string gid() const { return gid_; }
+
+ private:
+ format::CgroupController controller_;
+ mode_t mode_ = 0;
+ std::string uid_;
+ std::string gid_;
+};
+
+} // namespace cgrouprc
+} // namespace android
diff --git a/libprocessgroup/setup/cgroup_map_write.cpp b/libprocessgroup/setup/cgroup_map_write.cpp
new file mode 100644
index 0000000..da60948
--- /dev/null
+++ b/libprocessgroup/setup/cgroup_map_write.cpp
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "libprocessgroup"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <regex>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <json/reader.h>
+#include <json/value.h>
+#include <processgroup/format/cgroup_file.h>
+#include <processgroup/processgroup.h>
+#include <processgroup/setup.h>
+
+#include "cgroup_descriptor.h"
+
+using android::base::GetBoolProperty;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+
+namespace android {
+namespace cgrouprc {
+
+static constexpr const char* CGROUPS_DESC_FILE = "/etc/cgroups.json";
+static constexpr const char* CGROUPS_DESC_VENDOR_FILE = "/vendor/etc/cgroups.json";
+
+static bool Mkdir(const std::string& path, mode_t mode, const std::string& uid,
+ const std::string& gid) {
+ if (mode == 0) {
+ mode = 0755;
+ }
+
+ if (mkdir(path.c_str(), mode) != 0) {
+ /* chmod in case the directory already exists */
+ if (errno == EEXIST) {
+ if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
+ // /acct is a special case when the directory already exists
+ // TODO: check if file mode is already what we want instead of using EROFS
+ if (errno != EROFS) {
+ PLOG(ERROR) << "fchmodat() failed for " << path;
+ return false;
+ }
+ }
+ } else {
+ PLOG(ERROR) << "mkdir() failed for " << path;
+ return false;
+ }
+ }
+
+ if (uid.empty()) {
+ return true;
+ }
+
+ passwd* uid_pwd = getpwnam(uid.c_str());
+ if (!uid_pwd) {
+ PLOG(ERROR) << "Unable to decode UID for '" << uid << "'";
+ return false;
+ }
+
+ uid_t pw_uid = uid_pwd->pw_uid;
+ gid_t gr_gid = -1;
+ if (!gid.empty()) {
+ group* gid_pwd = getgrnam(gid.c_str());
+ if (!gid_pwd) {
+ PLOG(ERROR) << "Unable to decode GID for '" << gid << "'";
+ return false;
+ }
+ gr_gid = gid_pwd->gr_gid;
+ }
+
+ if (lchown(path.c_str(), pw_uid, gr_gid) < 0) {
+ PLOG(ERROR) << "lchown() failed for " << path;
+ return false;
+ }
+
+ /* chown may have cleared S_ISUID and S_ISGID, chmod again */
+ if (mode & (S_ISUID | S_ISGID)) {
+ if (fchmodat(AT_FDCWD, path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {
+ PLOG(ERROR) << "fchmodat() failed for " << path;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool ReadDescriptorsFromFile(const std::string& file_name,
+ std::map<std::string, CgroupDescriptor>* descriptors) {
+ std::vector<CgroupDescriptor> result;
+ std::string json_doc;
+
+ if (!android::base::ReadFileToString(file_name, &json_doc)) {
+ PLOG(ERROR) << "Failed to read task profiles from " << file_name;
+ return false;
+ }
+
+ Json::Reader reader;
+ Json::Value root;
+ if (!reader.parse(json_doc, root)) {
+ LOG(ERROR) << "Failed to parse cgroups description: " << reader.getFormattedErrorMessages();
+ return false;
+ }
+
+ if (root.isMember("Cgroups")) {
+ const Json::Value& cgroups = root["Cgroups"];
+ for (Json::Value::ArrayIndex i = 0; i < cgroups.size(); ++i) {
+ std::string name = cgroups[i]["Controller"].asString();
+ auto iter = descriptors->find(name);
+ if (iter == descriptors->end()) {
+ descriptors->emplace(
+ name, CgroupDescriptor(
+ 1, name, cgroups[i]["Path"].asString(),
+ std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
+ cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString()));
+ } else {
+ iter->second = CgroupDescriptor(
+ 1, name, cgroups[i]["Path"].asString(),
+ std::strtoul(cgroups[i]["Mode"].asString().c_str(), 0, 8),
+ cgroups[i]["UID"].asString(), cgroups[i]["GID"].asString());
+ }
+ }
+ }
+
+ if (root.isMember("Cgroups2")) {
+ const Json::Value& cgroups2 = root["Cgroups2"];
+ auto iter = descriptors->find(CGROUPV2_CONTROLLER_NAME);
+ if (iter == descriptors->end()) {
+ descriptors->emplace(
+ CGROUPV2_CONTROLLER_NAME,
+ CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
+ std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
+ cgroups2["UID"].asString(), cgroups2["GID"].asString()));
+ } else {
+ iter->second =
+ CgroupDescriptor(2, CGROUPV2_CONTROLLER_NAME, cgroups2["Path"].asString(),
+ std::strtoul(cgroups2["Mode"].asString().c_str(), 0, 8),
+ cgroups2["UID"].asString(), cgroups2["GID"].asString());
+ }
+ }
+
+ return true;
+}
+
+static bool ReadDescriptors(std::map<std::string, CgroupDescriptor>* descriptors) {
+ // load system cgroup descriptors
+ if (!ReadDescriptorsFromFile(CGROUPS_DESC_FILE, descriptors)) {
+ return false;
+ }
+
+ // load vendor cgroup descriptors if the file exists
+ if (!access(CGROUPS_DESC_VENDOR_FILE, F_OK) &&
+ !ReadDescriptorsFromFile(CGROUPS_DESC_VENDOR_FILE, descriptors)) {
+ return false;
+ }
+
+ return true;
+}
+
+// To avoid issues in sdk_mac build
+#if defined(__ANDROID__)
+
+static bool SetupCgroup(const CgroupDescriptor& descriptor) {
+ const format::CgroupController* controller = descriptor.controller();
+
+ // mkdir <path> [mode] [owner] [group]
+ if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {
+ LOG(ERROR) << "Failed to create directory for " << controller->name() << " cgroup";
+ return false;
+ }
+
+ int result;
+ if (controller->version() == 2) {
+ result = mount("none", controller->path(), "cgroup2", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+ nullptr);
+ } else {
+ // Unfortunately historically cpuset controller was mounted using a mount command
+ // different from all other controllers. This results in controller attributes not
+ // to be prepended with controller name. For example this way instead of
+ // /dev/cpuset/cpuset.cpus the attribute becomes /dev/cpuset/cpus which is what
+ // the system currently expects.
+ if (!strcmp(controller->name(), "cpuset")) {
+ // mount cpuset none /dev/cpuset nodev noexec nosuid
+ result = mount("none", controller->path(), controller->name(),
+ MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr);
+ } else {
+ // mount cgroup none <path> nodev noexec nosuid <controller>
+ result = mount("none", controller->path(), "cgroup", MS_NODEV | MS_NOEXEC | MS_NOSUID,
+ controller->name());
+ }
+ }
+
+ if (result < 0) {
+ PLOG(ERROR) << "Failed to mount " << controller->name() << " cgroup";
+ return false;
+ }
+
+ return true;
+}
+
+#else
+
+// Stubs for non-Android targets.
+static bool SetupCgroup(const CgroupDescriptor&) {
+ return false;
+}
+
+#endif
+
+static bool WriteRcFile(const std::map<std::string, CgroupDescriptor>& descriptors) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(CGROUPS_RC_PATH, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC,
+ S_IRUSR | S_IRGRP | S_IROTH)));
+ if (fd < 0) {
+ PLOG(ERROR) << "open() failed for " << CGROUPS_RC_PATH;
+ return false;
+ }
+
+ format::CgroupFile fl;
+ fl.version_ = format::CgroupFile::FILE_CURR_VERSION;
+ fl.controller_count_ = descriptors.size();
+ int ret = TEMP_FAILURE_RETRY(write(fd, &fl, sizeof(fl)));
+ if (ret < 0) {
+ PLOG(ERROR) << "write() failed for " << CGROUPS_RC_PATH;
+ return false;
+ }
+
+ for (const auto& [name, descriptor] : descriptors) {
+ ret = TEMP_FAILURE_RETRY(
+ write(fd, descriptor.controller(), sizeof(format::CgroupController)));
+ if (ret < 0) {
+ PLOG(ERROR) << "write() failed for " << CGROUPS_RC_PATH;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+CgroupDescriptor::CgroupDescriptor(uint32_t version, const std::string& name,
+ const std::string& path, mode_t mode, const std::string& uid,
+ const std::string& gid)
+ : controller_(version, name, path), mode_(mode), uid_(uid), gid_(gid) {}
+
+} // namespace cgrouprc
+} // namespace android
+
+bool CgroupSetup() {
+ using namespace android::cgrouprc;
+
+ std::map<std::string, CgroupDescriptor> descriptors;
+
+ if (getpid() != 1) {
+ LOG(ERROR) << "Cgroup setup can be done only by init process";
+ return false;
+ }
+
+ // Make sure we do this only one time. No need for std::call_once because
+ // init is a single-threaded process
+ if (access(CGROUPS_RC_PATH, F_OK) == 0) {
+ LOG(WARNING) << "Attempt to call SetupCgroups more than once";
+ return true;
+ }
+
+ // load cgroups.json file
+ if (!ReadDescriptors(&descriptors)) {
+ LOG(ERROR) << "Failed to load cgroup description file";
+ return false;
+ }
+
+ // setup cgroups
+ for (const auto& [name, descriptor] : descriptors) {
+ if (!SetupCgroup(descriptor)) {
+ // issue a warning and proceed with the next cgroup
+ // TODO: mark the descriptor as invalid and skip it in WriteRcFile()
+ LOG(WARNING) << "Failed to setup " << name << " cgroup";
+ }
+ }
+
+ // mkdir <CGROUPS_RC_DIR> 0711 system system
+ if (!Mkdir(android::base::Dirname(CGROUPS_RC_PATH), 0711, "system", "system")) {
+ LOG(ERROR) << "Failed to create directory for " << CGROUPS_RC_PATH << " file";
+ return false;
+ }
+
+ // Generate <CGROUPS_RC_FILE> file which can be directly mmapped into
+ // process memory. This optimizes performance, memory usage
+ // and limits infrormation shared with unprivileged processes
+ // to the minimum subset of information from cgroups.json
+ if (!WriteRcFile(descriptors)) {
+ LOG(ERROR) << "Failed to write " << CGROUPS_RC_PATH << " file";
+ return false;
+ }
+
+ // chmod 0644 <CGROUPS_RC_PATH>
+ if (fchmodat(AT_FDCWD, CGROUPS_RC_PATH, 0644, AT_SYMLINK_NOFOLLOW) < 0) {
+ PLOG(ERROR) << "fchmodat() failed";
+ return false;
+ }
+
+ return true;
+}
diff --git a/libprocessgroup/setup/include/processgroup/setup.h b/libprocessgroup/setup/include/processgroup/setup.h
new file mode 100644
index 0000000..6ea1979
--- /dev/null
+++ b/libprocessgroup/setup/include/processgroup/setup.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+bool CgroupSetup();
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index 9362c03..4b45c87 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -46,7 +46,7 @@
bool ProfileAttribute::GetPathForTask(int tid, std::string* path) const {
std::string subgroup;
- if (!controller_->GetTaskGroup(tid, &subgroup)) {
+ if (!controller()->GetTaskGroup(tid, &subgroup)) {
return false;
}
@@ -55,9 +55,10 @@
}
if (subgroup.empty()) {
- *path = StringPrintf("%s/%s", controller_->path(), file_name_.c_str());
+ *path = StringPrintf("%s/%s", controller()->path(), file_name_.c_str());
} else {
- *path = StringPrintf("%s/%s/%s", controller_->path(), subgroup.c_str(), file_name_.c_str());
+ *path = StringPrintf("%s/%s/%s", controller()->path(), subgroup.c_str(),
+ file_name_.c_str());
}
return true;
}
@@ -135,7 +136,7 @@
return path.find("<uid>", 0) != std::string::npos || path.find("<pid>", 0) != std::string::npos;
}
-SetCgroupAction::SetCgroupAction(const CgroupController* c, const std::string& p)
+SetCgroupAction::SetCgroupAction(const CgroupController& c, const std::string& p)
: controller_(c), path_(p) {
#ifdef CACHE_FILE_DESCRIPTORS
// cache file descriptor only if path is app independent
@@ -145,7 +146,7 @@
return;
}
- std::string tasks_path = c->GetTasksFilePath(p.c_str());
+ std::string tasks_path = c.GetTasksFilePath(p);
if (access(tasks_path.c_str(), W_OK) != 0) {
// file is not accessible
@@ -199,7 +200,7 @@
}
// this is app-dependent path, file descriptor is not cached
- std::string procs_path = controller_->GetProcsFilePath(path_, uid, pid);
+ std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
if (tmp_fd < 0) {
PLOG(WARNING) << "Failed to open " << procs_path;
@@ -212,7 +213,7 @@
return true;
#else
- std::string procs_path = controller_->GetProcsFilePath(path_, uid, pid);
+ std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);
unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));
if (tmp_fd < 0) {
// no permissions to access the file, ignore
@@ -247,7 +248,7 @@
LOG(ERROR) << "Application profile can't be applied to a thread";
return false;
#else
- std::string tasks_path = controller_->GetTasksFilePath(path_);
+ std::string tasks_path = controller()->GetTasksFilePath(path_);
unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));
if (tmp_fd < 0) {
// no permissions to access the file, ignore
@@ -326,8 +327,8 @@
std::string file_attr = attr[i]["File"].asString();
if (attributes_.find(name) == attributes_.end()) {
- const CgroupController* controller = cg_map.FindController(controller_name);
- if (controller) {
+ auto controller = cg_map.FindController(controller_name);
+ if (controller.HasValue()) {
attributes_[name] = std::make_unique<ProfileAttribute>(controller, file_attr);
} else {
LOG(WARNING) << "Controller " << controller_name << " is not found";
@@ -355,8 +356,8 @@
std::string controller_name = params_val["Controller"].asString();
std::string path = params_val["Path"].asString();
- const CgroupController* controller = cg_map.FindController(controller_name);
- if (controller) {
+ auto controller = cg_map.FindController(controller_name);
+ if (controller.HasValue()) {
profile->Add(std::make_unique<SetCgroupAction>(controller, path));
} else {
LOG(WARNING) << "JoinCgroup: controller " << controller_name << " is not found";
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 9ee81c1..37cc305 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -27,16 +27,16 @@
class ProfileAttribute {
public:
- ProfileAttribute(const CgroupController* controller, const std::string& file_name)
+ ProfileAttribute(const CgroupController& controller, const std::string& file_name)
: controller_(controller), file_name_(file_name) {}
- const CgroupController* controller() const { return controller_; }
+ const CgroupController* controller() const { return &controller_; }
const std::string& file_name() const { return file_name_; }
bool GetPathForTask(int tid, std::string* path) const;
private:
- const CgroupController* controller_;
+ CgroupController controller_;
std::string file_name_;
};
@@ -106,16 +106,16 @@
// Set cgroup profile element
class SetCgroupAction : public ProfileAction {
public:
- SetCgroupAction(const CgroupController* c, const std::string& p);
+ SetCgroupAction(const CgroupController& c, const std::string& p);
virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
virtual bool ExecuteForTask(int tid) const;
- const CgroupController* controller() const { return controller_; }
+ const CgroupController* controller() const { return &controller_; }
std::string path() const { return path_; }
private:
- const CgroupController* controller_;
+ CgroupController controller_;
std::string path_;
#ifdef CACHE_FILE_DESCRIPTORS
android::base::unique_fd fd_;
diff --git a/libstats/include/stats_event_list.h b/libstats/include/stats_event_list.h
index b5bc5af..845a197 100644
--- a/libstats/include/stats_event_list.h
+++ b/libstats/include/stats_event_list.h
@@ -18,15 +18,18 @@
#define ANDROID_STATS_LOG_STATS_EVENT_LIST_H
#include <log/log_event_list.h>
+#include <sys/uio.h>
#ifdef __cplusplus
extern "C" {
#endif
void reset_log_context(android_log_context ctx);
int write_to_logger(android_log_context context, log_id_t id);
-void note_log_drop(int error);
+void note_log_drop(int error, int atom_tag);
void stats_log_close();
int android_log_write_char_array(android_log_context ctx, const char* value, size_t len);
+extern int (*write_to_statsd)(struct iovec* vec, size_t nr);
+
#ifdef __cplusplus
}
#endif
diff --git a/libstats/stats_event_list.c b/libstats/stats_event_list.c
index 5b90361..ae12cbe 100644
--- a/libstats/stats_event_list.c
+++ b/libstats/stats_event_list.c
@@ -41,7 +41,7 @@
extern struct android_log_transport_write statsdLoggerWrite;
static int __write_to_statsd_init(struct iovec* vec, size_t nr);
-static int (*write_to_statsd)(struct iovec* vec, size_t nr) = __write_to_statsd_init;
+int (*write_to_statsd)(struct iovec* vec, size_t nr) = __write_to_statsd_init;
// Similar to create_android_logger(), but instead of allocation a new buffer,
// this function resets the buffer for resuse.
@@ -120,8 +120,8 @@
return retValue;
}
-void note_log_drop(int error) {
- statsdLoggerWrite.noteDrop(error);
+void note_log_drop(int error, int tag) {
+ statsdLoggerWrite.noteDrop(error, tag);
}
void stats_log_close() {
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
index f5be95c..b778f92 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/statsd_writer.c
@@ -47,9 +47,18 @@
#endif
#endif
+#ifndef htole64
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htole64(x) (x)
+#else
+#define htole64(x) __bswap_64(x)
+#endif
+#endif
+
static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
static atomic_int dropped = 0;
static atomic_int log_error = 0;
+static atomic_int atom_tag = 0;
void statsd_writer_init_lock() {
/*
@@ -152,9 +161,10 @@
return 1;
}
-static void statsdNoteDrop(int error) {
+static void statsdNoteDrop(int error, int tag) {
atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
atomic_exchange_explicit(&log_error, error, memory_order_relaxed);
+ atomic_exchange_explicit(&atom_tag, tag, memory_order_relaxed);
}
static int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {
@@ -203,12 +213,17 @@
if (sock >= 0) {
int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
if (snapshot) {
- android_log_event_int_t buffer;
+ android_log_event_long_t buffer;
header.id = LOG_ID_STATS;
// store the last log error in the tag field. This tag field is not used by statsd.
buffer.header.tag = htole32(atomic_load(&log_error));
- buffer.payload.type = EVENT_TYPE_INT;
- buffer.payload.data = htole32(snapshot);
+ buffer.payload.type = EVENT_TYPE_LONG;
+ // format:
+ // |atom_tag|dropped_count|
+ int64_t composed_long = atomic_load(&atom_tag);
+ // Send 2 int32's via an int64.
+ composed_long = ((composed_long << 32) | ((int64_t)snapshot));
+ buffer.payload.data = htole64(composed_long);
newVec[headerLength].iov_base = &buffer;
newVec[headerLength].iov_len = sizeof(buffer);
diff --git a/libstats/statsd_writer.h b/libstats/statsd_writer.h
index 4fc3f8b..fe2d37c 100644
--- a/libstats/statsd_writer.h
+++ b/libstats/statsd_writer.h
@@ -39,7 +39,7 @@
/* write log to transport, returns number of bytes propagated, or -errno */
int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);
/* note one log drop */
- void (*noteDrop)(int error);
+ void (*noteDrop)(int error, int tag);
};
#endif // ANDROID_STATS_LOG_STATS_WRITER_H
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index b7650a1..ce25108 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -49,7 +49,6 @@
srcs: [
"ArmExidx.cpp",
"DexFile.cpp",
- "DexFiles.cpp",
"DwarfCfa.cpp",
"DwarfEhFrameWithHdr.cpp",
"DwarfMemory.cpp",
@@ -92,7 +91,6 @@
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
exclude_srcs: [
"DexFile.cpp",
- "DexFiles.cpp",
],
exclude_shared_libs: [
"libdexfile_support",
@@ -102,7 +100,6 @@
cflags: ["-DNO_LIBDEXFILE_SUPPORT"],
exclude_srcs: [
"DexFile.cpp",
- "DexFiles.cpp",
],
exclude_shared_libs: [
"libdexfile_support",
diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp
index eaf867f..d8d5a24 100644
--- a/libunwindstack/DexFile.cpp
+++ b/libunwindstack/DexFile.cpp
@@ -35,22 +35,31 @@
std::unique_ptr<DexFile> DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory,
MapInfo* info) {
if (!info->name.empty()) {
- std::unique_ptr<DexFile> dex_file =
+ std::unique_ptr<DexFile> dex_file_from_file =
DexFileFromFile::Create(dex_file_offset_in_memory - info->start + info->offset, info->name);
- if (dex_file) {
- return dex_file;
+ if (dex_file_from_file) {
+ dex_file_from_file->addr_ = dex_file_offset_in_memory;
+ return dex_file_from_file;
}
}
- return DexFileFromMemory::Create(dex_file_offset_in_memory, memory, info->name);
+ std::unique_ptr<DexFile> dex_file_from_memory =
+ DexFileFromMemory::Create(dex_file_offset_in_memory, memory, info->name);
+ if (dex_file_from_memory) {
+ dex_file_from_memory->addr_ = dex_file_offset_in_memory;
+ return dex_file_from_memory;
+ }
+ return nullptr;
}
-bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
- uint64_t* method_offset) {
+bool DexFile::GetFunctionName(uint64_t dex_pc, std::string* method_name, uint64_t* method_offset) {
+ uint64_t dex_offset = dex_pc - addr_;
art_api::dex::MethodInfo method_info = GetMethodInfoForOffset(dex_offset, false);
if (method_info.offset == 0) {
return false;
}
- *method_name = method_info.name;
+ if (method_name != nullptr) {
+ *method_name = method_info.name;
+ }
*method_offset = dex_offset - method_info.offset;
return true;
}
diff --git a/libunwindstack/DexFile.h b/libunwindstack/DexFile.h
index ca658e6..1448a4b 100644
--- a/libunwindstack/DexFile.h
+++ b/libunwindstack/DexFile.h
@@ -29,17 +29,22 @@
namespace unwindstack {
+class Memory;
+struct MapInfo;
+
class DexFile : protected art_api::dex::DexFile {
public:
virtual ~DexFile() = default;
- bool GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset);
+ bool GetFunctionName(uint64_t dex_pc, std::string* method_name, uint64_t* method_offset);
static std::unique_ptr<DexFile> Create(uint64_t dex_file_offset_in_memory, Memory* memory,
MapInfo* info);
protected:
DexFile(art_api::dex::DexFile&& art_dex_file) : art_api::dex::DexFile(std::move(art_dex_file)) {}
+
+ uint64_t addr_ = 0;
};
class DexFileFromFile : public DexFile {
diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp
deleted file mode 100644
index 63a77e5..0000000
--- a/libunwindstack/DexFiles.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdint.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <memory>
-
-#include <unwindstack/DexFiles.h>
-#include <unwindstack/MapInfo.h>
-#include <unwindstack/Maps.h>
-#include <unwindstack/Memory.h>
-
-#include "DexFile.h"
-
-namespace unwindstack {
-
-struct DEXFileEntry32 {
- uint32_t next;
- uint32_t prev;
- uint32_t dex_file;
-};
-
-struct DEXFileEntry64 {
- uint64_t next;
- uint64_t prev;
- uint64_t dex_file;
-};
-
-DexFiles::DexFiles(std::shared_ptr<Memory>& memory) : Global(memory) {}
-
-DexFiles::DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
- : Global(memory, search_libs) {}
-
-DexFiles::~DexFiles() {}
-
-void DexFiles::ProcessArch() {
- switch (arch()) {
- case ARCH_ARM:
- case ARCH_MIPS:
- case ARCH_X86:
- read_entry_ptr_func_ = &DexFiles::ReadEntryPtr32;
- read_entry_func_ = &DexFiles::ReadEntry32;
- break;
-
- case ARCH_ARM64:
- case ARCH_MIPS64:
- case ARCH_X86_64:
- read_entry_ptr_func_ = &DexFiles::ReadEntryPtr64;
- read_entry_func_ = &DexFiles::ReadEntry64;
- break;
-
- case ARCH_UNKNOWN:
- abort();
- }
-}
-
-uint64_t DexFiles::ReadEntryPtr32(uint64_t addr) {
- uint32_t entry;
- const uint32_t field_offset = 12; // offset of first_entry_ in the descriptor struct.
- if (!memory_->ReadFully(addr + field_offset, &entry, sizeof(entry))) {
- return 0;
- }
- return entry;
-}
-
-uint64_t DexFiles::ReadEntryPtr64(uint64_t addr) {
- uint64_t entry;
- const uint32_t field_offset = 16; // offset of first_entry_ in the descriptor struct.
- if (!memory_->ReadFully(addr + field_offset, &entry, sizeof(entry))) {
- return 0;
- }
- return entry;
-}
-
-bool DexFiles::ReadEntry32() {
- DEXFileEntry32 entry;
- if (!memory_->ReadFully(entry_addr_, &entry, sizeof(entry)) || entry.dex_file == 0) {
- entry_addr_ = 0;
- return false;
- }
-
- addrs_.push_back(entry.dex_file);
- entry_addr_ = entry.next;
- return true;
-}
-
-bool DexFiles::ReadEntry64() {
- DEXFileEntry64 entry;
- if (!memory_->ReadFully(entry_addr_, &entry, sizeof(entry)) || entry.dex_file == 0) {
- entry_addr_ = 0;
- return false;
- }
-
- addrs_.push_back(entry.dex_file);
- entry_addr_ = entry.next;
- return true;
-}
-
-bool DexFiles::ReadVariableData(uint64_t ptr_offset) {
- entry_addr_ = (this->*read_entry_ptr_func_)(ptr_offset);
- return entry_addr_ != 0;
-}
-
-void DexFiles::Init(Maps* maps) {
- if (initialized_) {
- return;
- }
- initialized_ = true;
- entry_addr_ = 0;
-
- FindAndReadVariable(maps, "__dex_debug_descriptor");
-}
-
-DexFile* DexFiles::GetDexFile(uint64_t dex_file_offset, MapInfo* info) {
- // Lock while processing the data.
- DexFile* dex_file;
- auto entry = files_.find(dex_file_offset);
- if (entry == files_.end()) {
- std::unique_ptr<DexFile> new_dex_file = DexFile::Create(dex_file_offset, memory_.get(), info);
- dex_file = new_dex_file.get();
- files_[dex_file_offset] = std::move(new_dex_file);
- } else {
- dex_file = entry->second.get();
- }
- return dex_file;
-}
-
-bool DexFiles::GetAddr(size_t index, uint64_t* addr) {
- if (index < addrs_.size()) {
- *addr = addrs_[index];
- return true;
- }
- if (entry_addr_ != 0 && (this->*read_entry_func_)()) {
- *addr = addrs_.back();
- return true;
- }
- return false;
-}
-
-void DexFiles::GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc,
- std::string* method_name, uint64_t* method_offset) {
- std::lock_guard<std::mutex> guard(lock_);
- if (!initialized_) {
- Init(maps);
- }
-
- size_t index = 0;
- uint64_t addr;
- while (GetAddr(index++, &addr)) {
- if (addr < info->start || addr >= info->end) {
- continue;
- }
-
- DexFile* dex_file = GetDexFile(addr, info);
- if (dex_file != nullptr &&
- dex_file->GetMethodInformation(dex_pc - addr, method_name, method_offset)) {
- break;
- }
- }
-}
-
-} // namespace unwindstack
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 57a780e..849a31a 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -138,7 +138,7 @@
return false;
}
- if (cie->version != 1 && cie->version != 3 && cie->version != 4) {
+ if (cie->version != 1 && cie->version != 3 && cie->version != 4 && cie->version != 5) {
// Unrecognized version.
last_error_.code = DWARF_ERROR_UNSUPPORTED_VERSION;
return false;
@@ -155,7 +155,7 @@
cie->augmentation_string.push_back(aug_value);
} while (aug_value != '\0');
- if (cie->version == 4) {
+ if (cie->version == 4 || cie->version == 5) {
// Skip the Address Size field since we only use it for validation.
memory_.set_cur_offset(memory_.cur_offset() + 1);
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 4b93abb..5b402ed 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -243,6 +243,24 @@
return false;
}
+bool Elf::GetTextRange(uint64_t* addr, uint64_t* size) {
+ if (!valid_) {
+ return false;
+ }
+
+ if (interface_->GetTextRange(addr, size)) {
+ *addr += load_bias_;
+ return true;
+ }
+
+ if (gnu_debugdata_interface_ != nullptr && gnu_debugdata_interface_->GetTextRange(addr, size)) {
+ *addr += load_bias_;
+ return true;
+ }
+
+ return false;
+}
+
ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) {
if (!IsValidElf(memory)) {
return nullptr;
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index 12efb94..32c637f 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -69,6 +69,15 @@
return false;
}
+bool ElfInterface::GetTextRange(uint64_t* addr, uint64_t* size) {
+ if (text_size_ != 0) {
+ *addr = text_addr_;
+ *size = text_size_;
+ return true;
+ }
+ return false;
+}
+
Memory* ElfInterface::CreateGnuDebugdataMemory() {
if (gnu_debugdata_offset_ == 0 || gnu_debugdata_size_ == 0) {
return nullptr;
@@ -276,7 +285,7 @@
if (gnu_build_id_size_ - offset < hdr.n_descsz || hdr.n_descsz == 0) {
return "";
}
- std::string build_id(hdr.n_descsz - 1, '\0');
+ std::string build_id(hdr.n_descsz, '\0');
if (memory_->ReadFully(gnu_build_id_offset_ + offset, &build_id[0], hdr.n_descsz)) {
return build_id;
}
@@ -330,29 +339,26 @@
}
symbols_.push_back(new Symbols(shdr.sh_offset, shdr.sh_size, shdr.sh_entsize,
str_shdr.sh_offset, str_shdr.sh_size));
- } else if (shdr.sh_type == SHT_PROGBITS && sec_size != 0) {
+ } else if (shdr.sh_type == SHT_PROGBITS || shdr.sh_type == SHT_NOBITS) {
// Look for the .debug_frame and .gnu_debugdata.
if (shdr.sh_name < sec_size) {
std::string name;
if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
- uint64_t* offset_ptr = nullptr;
- uint64_t* size_ptr = nullptr;
if (name == ".debug_frame") {
- offset_ptr = &debug_frame_offset_;
- size_ptr = &debug_frame_size_;
+ debug_frame_offset_ = shdr.sh_offset;
+ debug_frame_size_ = shdr.sh_size;
} else if (name == ".gnu_debugdata") {
- offset_ptr = &gnu_debugdata_offset_;
- size_ptr = &gnu_debugdata_size_;
+ gnu_debugdata_offset_ = shdr.sh_offset;
+ gnu_debugdata_size_ = shdr.sh_size;
} else if (name == ".eh_frame") {
- offset_ptr = &eh_frame_offset_;
- size_ptr = &eh_frame_size_;
+ eh_frame_offset_ = shdr.sh_offset;
+ eh_frame_size_ = shdr.sh_size;
} else if (eh_frame_hdr_offset_ == 0 && name == ".eh_frame_hdr") {
- offset_ptr = &eh_frame_hdr_offset_;
- size_ptr = &eh_frame_hdr_size_;
- }
- if (offset_ptr != nullptr) {
- *offset_ptr = shdr.sh_offset;
- *size_ptr = shdr.sh_size;
+ eh_frame_hdr_offset_ = shdr.sh_offset;
+ eh_frame_hdr_size_ = shdr.sh_size;
+ } else if (name == ".text") {
+ text_addr_ = shdr.sh_addr;
+ text_size_ = shdr.sh_size;
}
}
}
diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp
index 20bc4b9..71665a1 100644
--- a/libunwindstack/JitDebug.cpp
+++ b/libunwindstack/JitDebug.cpp
@@ -16,8 +16,13 @@
#include <stdint.h>
#include <sys/mman.h>
+#include <cstddef>
+#include <atomic>
+#include <deque>
+#include <map>
#include <memory>
+#include <unordered_set>
#include <vector>
#include <unwindstack/Elf.h>
@@ -25,197 +30,334 @@
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+#include <DexFile.h>
+#endif
+
// This implements the JIT Compilation Interface.
// See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html
namespace unwindstack {
-struct JITCodeEntry32Pack {
- uint32_t next;
- uint32_t prev;
- uint32_t symfile_addr;
- uint64_t symfile_size;
+// 32-bit platforms may differ in alignment of uint64_t.
+struct Uint64_P {
+ uint64_t value;
} __attribute__((packed));
+struct Uint64_A {
+ uint64_t value;
+} __attribute__((aligned(8)));
-struct JITCodeEntry32Pad {
- uint32_t next;
- uint32_t prev;
- uint32_t symfile_addr;
- uint32_t pad;
- uint64_t symfile_size;
+// Wrapper around other memory object which protects us against data races.
+// It will check seqlock after every read, and fail if the seqlock changed.
+// This ensues that the read memory has not been partially modified.
+struct JitMemory : public Memory {
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ Memory* parent_ = nullptr;
+ uint64_t seqlock_addr_ = 0;
+ uint32_t expected_seqlock_ = 0;
+ bool failed_due_to_race_ = false;
};
-struct JITCodeEntry64 {
- uint64_t next;
- uint64_t prev;
- uint64_t symfile_addr;
- uint64_t symfile_size;
+template <typename Symfile>
+struct JitCacheEntry {
+ // PC memory range described by this entry.
+ uint64_t addr_ = 0;
+ uint64_t size_ = 0;
+ std::unique_ptr<Symfile> symfile_;
+
+ bool Init(Maps* maps, JitMemory* memory, uint64_t addr, uint64_t size);
};
-struct JITDescriptorHeader {
- uint32_t version;
- uint32_t action_flag;
+template <typename Symfile, typename PointerT, typename Uint64_T>
+class JitDebugImpl : public JitDebug<Symfile>, public Global {
+ public:
+ static constexpr const char* kDescriptorExtMagic = "Android1";
+ static constexpr int kMaxRaceRetries = 16;
+
+ struct JITCodeEntry {
+ PointerT next;
+ PointerT prev;
+ PointerT symfile_addr;
+ Uint64_T symfile_size;
+ };
+
+ struct JITDescriptor {
+ uint32_t version;
+ uint32_t action_flag;
+ PointerT relevant_entry;
+ PointerT first_entry;
+ };
+
+ // Android-specific extensions.
+ struct JITDescriptorExt {
+ JITDescriptor desc;
+ uint8_t magic[8];
+ uint32_t flags;
+ uint32_t sizeof_descriptor;
+ uint32_t sizeof_entry;
+ uint32_t action_seqlock;
+ uint64_t action_timestamp;
+ };
+
+ JitDebugImpl(ArchEnum arch, std::shared_ptr<Memory>& memory,
+ std::vector<std::string>& search_libs)
+ : Global(memory, search_libs) {
+ SetArch(arch);
+ }
+
+ Symfile* Get(Maps* maps, uint64_t pc) override;
+ virtual bool ReadVariableData(uint64_t offset);
+ virtual void ProcessArch() {}
+ bool Update(Maps* maps);
+ bool Read(Maps* maps, JitMemory* memory);
+
+ bool initialized_ = false;
+ uint64_t descriptor_addr_ = 0; // Non-zero if we have found (non-empty) descriptor.
+ uint64_t seqlock_addr_ = 0; // Re-read entries if the value at this address changes.
+ uint32_t last_seqlock_ = ~0u; // The value of seqlock when we last read the entries.
+
+ std::deque<JitCacheEntry<Symfile>> entries_;
+
+ std::mutex lock_;
};
-struct JITDescriptor32 {
- JITDescriptorHeader header;
- uint32_t relevant_entry;
- uint32_t first_entry;
-};
-
-struct JITDescriptor64 {
- JITDescriptorHeader header;
- uint64_t relevant_entry;
- uint64_t first_entry;
-};
-
-JitDebug::JitDebug(std::shared_ptr<Memory>& memory) : Global(memory) {}
-
-JitDebug::JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
- : Global(memory, search_libs) {}
-
-JitDebug::~JitDebug() {
- for (auto* elf : elf_list_) {
- delete elf;
- }
-}
-
-uint64_t JitDebug::ReadDescriptor32(uint64_t addr) {
- JITDescriptor32 desc;
- if (!memory_->ReadFully(addr, &desc, sizeof(desc))) {
- return 0;
- }
-
- if (desc.header.version != 1 || desc.first_entry == 0) {
- // Either unknown version, or no jit entries.
- return 0;
- }
-
- return desc.first_entry;
-}
-
-uint64_t JitDebug::ReadDescriptor64(uint64_t addr) {
- JITDescriptor64 desc;
- if (!memory_->ReadFully(addr, &desc, sizeof(desc))) {
- return 0;
- }
-
- if (desc.header.version != 1 || desc.first_entry == 0) {
- // Either unknown version, or no jit entries.
- return 0;
- }
-
- return desc.first_entry;
-}
-
-uint64_t JitDebug::ReadEntry32Pack(uint64_t* start, uint64_t* size) {
- JITCodeEntry32Pack code;
- if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
- return 0;
- }
-
- *start = code.symfile_addr;
- *size = code.symfile_size;
- return code.next;
-}
-
-uint64_t JitDebug::ReadEntry32Pad(uint64_t* start, uint64_t* size) {
- JITCodeEntry32Pad code;
- if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
- return 0;
- }
-
- *start = code.symfile_addr;
- *size = code.symfile_size;
- return code.next;
-}
-
-uint64_t JitDebug::ReadEntry64(uint64_t* start, uint64_t* size) {
- JITCodeEntry64 code;
- if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
- return 0;
- }
-
- *start = code.symfile_addr;
- *size = code.symfile_size;
- return code.next;
-}
-
-void JitDebug::ProcessArch() {
- switch (arch()) {
+template <typename Symfile>
+std::unique_ptr<JitDebug<Symfile>> JitDebug<Symfile>::Create(ArchEnum arch,
+ std::shared_ptr<Memory>& memory,
+ std::vector<std::string> search_libs) {
+ typedef JitDebugImpl<Symfile, uint32_t, Uint64_P> JitDebugImpl32P;
+ typedef JitDebugImpl<Symfile, uint32_t, Uint64_A> JitDebugImpl32A;
+ typedef JitDebugImpl<Symfile, uint64_t, Uint64_A> JitDebugImpl64A;
+ switch (arch) {
case ARCH_X86:
- read_descriptor_func_ = &JitDebug::ReadDescriptor32;
- read_entry_func_ = &JitDebug::ReadEntry32Pack;
+ static_assert(sizeof(typename JitDebugImpl32P::JITCodeEntry) == 20, "layout");
+ static_assert(sizeof(typename JitDebugImpl32P::JITDescriptor) == 16, "layout");
+ static_assert(sizeof(typename JitDebugImpl32P::JITDescriptorExt) == 48, "layout");
+ return std::unique_ptr<JitDebug>(new JitDebugImpl32P(arch, memory, search_libs));
break;
-
case ARCH_ARM:
case ARCH_MIPS:
- read_descriptor_func_ = &JitDebug::ReadDescriptor32;
- read_entry_func_ = &JitDebug::ReadEntry32Pad;
+ static_assert(sizeof(typename JitDebugImpl32A::JITCodeEntry) == 24, "layout");
+ static_assert(sizeof(typename JitDebugImpl32A::JITDescriptor) == 16, "layout");
+ static_assert(sizeof(typename JitDebugImpl32A::JITDescriptorExt) == 48, "layout");
+ return std::unique_ptr<JitDebug>(new JitDebugImpl32A(arch, memory, search_libs));
break;
-
case ARCH_ARM64:
case ARCH_X86_64:
case ARCH_MIPS64:
- read_descriptor_func_ = &JitDebug::ReadDescriptor64;
- read_entry_func_ = &JitDebug::ReadEntry64;
+ static_assert(sizeof(typename JitDebugImpl64A::JITCodeEntry) == 32, "layout");
+ static_assert(sizeof(typename JitDebugImpl64A::JITDescriptor) == 24, "layout");
+ static_assert(sizeof(typename JitDebugImpl64A::JITDescriptorExt) == 56, "layout");
+ return std::unique_ptr<JitDebug>(new JitDebugImpl64A(arch, memory, search_libs));
break;
- case ARCH_UNKNOWN:
+ default:
abort();
}
}
-bool JitDebug::ReadVariableData(uint64_t ptr) {
- entry_addr_ = (this->*read_descriptor_func_)(ptr);
- return entry_addr_ != 0;
-}
-
-void JitDebug::Init(Maps* maps) {
- if (initialized_) {
- return;
+size_t JitMemory::Read(uint64_t addr, void* dst, size_t size) {
+ if (!parent_->ReadFully(addr, dst, size)) {
+ return 0;
}
- // Regardless of what happens below, consider the init finished.
- initialized_ = true;
-
- FindAndReadVariable(maps, "__jit_debug_descriptor");
+ // This is required for memory synchronization if the we are working with local memory.
+ // For other types of memory (e.g. remote) this is no-op and has no significant effect.
+ std::atomic_thread_fence(std::memory_order_acquire);
+ uint32_t seen_seqlock;
+ if (!parent_->Read32(seqlock_addr_, &seen_seqlock)) {
+ return 0;
+ }
+ if (seen_seqlock != expected_seqlock_) {
+ failed_due_to_race_ = true;
+ return 0;
+ }
+ return size;
}
-Elf* JitDebug::GetElf(Maps* maps, uint64_t pc) {
- // Use a single lock, this object should be used so infrequently that
- // a fine grain lock is unnecessary.
+template <typename Symfile, typename PointerT, typename Uint64_T>
+bool JitDebugImpl<Symfile, PointerT, Uint64_T>::ReadVariableData(uint64_t addr) {
+ JITDescriptor desc;
+ if (!this->memory_->ReadFully(addr, &desc, sizeof(desc))) {
+ return false;
+ }
+ if (desc.version != 1) {
+ return false;
+ }
+ if (desc.first_entry == 0) {
+ return false; // There could be multiple descriptors. Ignore empty ones.
+ }
+ descriptor_addr_ = addr;
+ JITDescriptorExt desc_ext;
+ if (this->memory_->ReadFully(addr, &desc_ext, sizeof(desc_ext)) &&
+ memcmp(desc_ext.magic, kDescriptorExtMagic, 8) == 0) {
+ seqlock_addr_ = descriptor_addr_ + offsetof(JITDescriptorExt, action_seqlock);
+ } else {
+ // In the absence of Android-specific fields, use the head pointer instead.
+ seqlock_addr_ = descriptor_addr_ + offsetof(JITDescriptor, first_entry);
+ }
+ return true;
+}
+
+template <typename Symfile>
+static const char* GetDescriptorName();
+
+template <>
+const char* GetDescriptorName<Elf>() {
+ return "__jit_debug_descriptor";
+}
+
+template <typename Symfile, typename PointerT, typename Uint64_T>
+Symfile* JitDebugImpl<Symfile, PointerT, Uint64_T>::Get(Maps* maps, uint64_t pc) {
std::lock_guard<std::mutex> guard(lock_);
if (!initialized_) {
- Init(maps);
+ FindAndReadVariable(maps, GetDescriptorName<Symfile>());
+ initialized_ = true;
}
- // Search the existing elf object first.
- for (Elf* elf : elf_list_) {
- if (elf->IsValidPc(pc)) {
- return elf;
- }
+ if (descriptor_addr_ == 0) {
+ return nullptr;
}
- while (entry_addr_ != 0) {
- uint64_t start;
- uint64_t size;
- entry_addr_ = (this->*read_entry_func_)(&start, &size);
+ if (!Update(maps)) {
+ return nullptr;
+ }
- Elf* elf = new Elf(new MemoryRange(memory_, start, size, 0));
- elf->Init();
- if (!elf->valid()) {
- // The data is not formatted in a way we understand, do not attempt
- // to process any other entries.
- entry_addr_ = 0;
- delete elf;
- return nullptr;
- }
- elf_list_.push_back(elf);
-
- if (elf->IsValidPc(pc)) {
- return elf;
+ Symfile* fallback = nullptr;
+ for (auto& entry : entries_) {
+ // Skip entries which are obviously not relevant (if we know the PC range).
+ if (entry.size_ == 0 || (entry.addr_ <= pc && (pc - entry.addr_) < entry.size_)) {
+ // Double check the entry contains the PC in case there are overlapping entries.
+ // This is might happen for native-code due to GC and for DEX due to data sharing.
+ std::string method_name;
+ uint64_t method_offset;
+ if (entry.symfile_->GetFunctionName(pc, &method_name, &method_offset)) {
+ return entry.symfile_.get();
+ }
+ fallback = entry.symfile_.get(); // Tests don't have any symbols.
}
}
- return nullptr;
+ return fallback; // Not found.
}
+// Update JIT entries if needed. It will retry if there are data races.
+template <typename Symfile, typename PointerT, typename Uint64_T>
+bool JitDebugImpl<Symfile, PointerT, Uint64_T>::Update(Maps* maps) {
+ // We might need to retry the whole read in the presence of data races.
+ for (int i = 0; i < kMaxRaceRetries; i++) {
+ // Read the seqlock (counter which is incremented before and after any modification).
+ uint32_t seqlock = 0;
+ if (!this->memory_->Read32(seqlock_addr_, &seqlock)) {
+ return false; // Failed to read seqlock.
+ }
+
+ // Check if anything changed since the last time we checked.
+ if (last_seqlock_ != seqlock) {
+ // Create memory wrapper to allow us to read the entries safely even in a live process.
+ JitMemory safe_memory;
+ safe_memory.parent_ = this->memory_.get();
+ safe_memory.seqlock_addr_ = seqlock_addr_;
+ safe_memory.expected_seqlock_ = seqlock;
+ std::atomic_thread_fence(std::memory_order_acquire);
+
+ // Add all entries to our cache.
+ if (!Read(maps, &safe_memory)) {
+ if (safe_memory.failed_due_to_race_) {
+ sleep(0);
+ continue; // Try again (there was a data race).
+ } else {
+ return false; // Proper failure (we could not read the data).
+ }
+ }
+ last_seqlock_ = seqlock;
+ }
+ return true;
+ }
+ return false; // Too many retries.
+}
+
+// Read all JIT entries. It might randomly fail due to data races.
+template <typename Symfile, typename PointerT, typename Uint64_T>
+bool JitDebugImpl<Symfile, PointerT, Uint64_T>::Read(Maps* maps, JitMemory* memory) {
+ std::unordered_set<uint64_t> seen_entry_addr;
+
+ // Read and verify the descriptor (must be after we have read the initial seqlock).
+ JITDescriptor desc;
+ if (!(memory->ReadFully(descriptor_addr_, &desc, sizeof(desc)))) {
+ return false;
+ }
+
+ entries_.clear();
+ JITCodeEntry entry;
+ for (uint64_t entry_addr = desc.first_entry; entry_addr != 0; entry_addr = entry.next) {
+ // Check for infinite loops in the lined list.
+ if (!seen_entry_addr.emplace(entry_addr).second) {
+ return true; // TODO: Fail when seening infinite loop.
+ }
+
+ // Read the entry (while checking for data races).
+ if (!memory->ReadFully(entry_addr, &entry, sizeof(entry))) {
+ return false;
+ }
+
+ // Copy and load the symfile.
+ entries_.emplace_back(JitCacheEntry<Symfile>());
+ if (!entries_.back().Init(maps, memory, entry.symfile_addr, entry.symfile_size.value)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Copy and load ELF file.
+template <>
+bool JitCacheEntry<Elf>::Init(Maps*, JitMemory* memory, uint64_t addr, uint64_t size) {
+ // Make a copy of the in-memory symbol file (while checking for data races).
+ std::unique_ptr<MemoryBuffer> buffer(new MemoryBuffer());
+ buffer->Resize(size);
+ if (!memory->ReadFully(addr, buffer->GetPtr(0), buffer->Size())) {
+ return false;
+ }
+
+ // Load and validate the ELF file.
+ symfile_.reset(new Elf(buffer.release()));
+ symfile_->Init();
+ if (!symfile_->valid()) {
+ return false;
+ }
+
+ symfile_->GetTextRange(&addr_, &size_);
+ return true;
+}
+
+template std::unique_ptr<JitDebug<Elf>> JitDebug<Elf>::Create(ArchEnum, std::shared_ptr<Memory>&,
+ std::vector<std::string>);
+
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+
+template <>
+const char* GetDescriptorName<DexFile>() {
+ return "__dex_debug_descriptor";
+}
+
+// Copy and load DEX file.
+template <>
+bool JitCacheEntry<DexFile>::Init(Maps* maps, JitMemory* memory, uint64_t addr, uint64_t) {
+ MapInfo* info = maps->Find(addr);
+ if (info == nullptr) {
+ return false;
+ }
+ symfile_ = DexFile::Create(addr, memory, info);
+ if (symfile_ == nullptr) {
+ return false;
+ }
+ return true;
+}
+
+template std::unique_ptr<JitDebug<DexFile>> JitDebug<DexFile>::Create(ArchEnum,
+ std::shared_ptr<Memory>&,
+ std::vector<std::string>);
+
+#endif
+
} // namespace unwindstack
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index 3f2e1c1..73c5a04 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -36,11 +36,38 @@
#include <unwindstack/Unwinder.h>
#if !defined(NO_LIBDEXFILE_SUPPORT)
-#include <unwindstack/DexFiles.h>
+#include <DexFile.h>
#endif
namespace unwindstack {
+Unwinder::Unwinder(size_t max_frames, Maps* maps, Regs* regs,
+ std::shared_ptr<Memory> process_memory)
+ : max_frames_(max_frames), maps_(maps), regs_(regs), process_memory_(process_memory) {
+ frames_.reserve(max_frames);
+ if (regs != nullptr) {
+ ArchEnum arch = regs_->Arch();
+
+ jit_debug_ = JitDebug<Elf>::Create(arch, process_memory_);
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+ dex_files_ = JitDebug<DexFile>::Create(arch, process_memory_);
+#endif
+ }
+}
+
+void Unwinder::SetRegs(Regs* regs) {
+ regs_ = regs;
+
+ if (jit_debug_ == nullptr) {
+ ArchEnum arch = regs_->Arch();
+
+ jit_debug_ = JitDebug<Elf>::Create(arch, process_memory_);
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+ dex_files_ = JitDebug<DexFile>::Create(arch, process_memory_);
+#endif
+ }
+}
+
// Inject extra 'virtual' frame that represents the dex pc data.
// The dex pc is a magic register defined in the Mterp interpreter,
// and thus it will be restored/observed in the frame after it.
@@ -84,8 +111,7 @@
return;
}
- dex_files_->GetMethodInformation(maps_, info, dex_pc, &frame->function_name,
- &frame->function_offset);
+ dex_files_->GetFunctionName(maps_, dex_pc, &frame->function_name, &frame->function_offset);
#endif
}
@@ -185,7 +211,7 @@
// using the jit debug information.
if (!elf->valid() && jit_debug_ != nullptr) {
uint64_t adjusted_jit_pc = regs_->pc() - pc_adjustment;
- Elf* jit_elf = jit_debug_->GetElf(maps_, adjusted_jit_pc);
+ Elf* jit_elf = jit_debug_->Get(maps_, adjusted_jit_pc);
if (jit_elf != nullptr) {
// The jit debug information requires a non relative adjusted pc.
step_pc = adjusted_jit_pc;
@@ -330,19 +356,7 @@
return FormatFrame(frames_[frame_num]);
}
-void Unwinder::SetJitDebug(JitDebug* jit_debug, ArchEnum arch) {
- jit_debug->SetArch(arch);
- jit_debug_ = jit_debug;
-}
-
-#if !defined(NO_LIBDEXFILE_SUPPORT)
-void Unwinder::SetDexFiles(DexFiles* dex_files, ArchEnum arch) {
- dex_files->SetArch(arch);
- dex_files_ = dex_files;
-}
-#endif
-
-bool UnwinderFromPid::Init(ArchEnum arch) {
+bool UnwinderFromPid::Init() {
if (pid_ == getpid()) {
maps_ptr_.reset(new LocalMaps());
} else {
@@ -355,15 +369,6 @@
process_memory_ = Memory::CreateProcessMemoryCached(pid_);
- jit_debug_ptr_.reset(new JitDebug(process_memory_));
- jit_debug_ = jit_debug_ptr_.get();
- SetJitDebug(jit_debug_, arch);
-#if !defined(NO_LIBDEXFILE_SUPPORT)
- dex_files_ptr_.reset(new DexFiles(process_memory_));
- dex_files_ = dex_files_ptr_.get();
- SetDexFiles(dex_files_, arch);
-#endif
-
return true;
}
diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h
deleted file mode 100644
index 67a9640..0000000
--- a/libunwindstack/include/unwindstack/DexFiles.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _LIBUNWINDSTACK_DEX_FILES_H
-#define _LIBUNWINDSTACK_DEX_FILES_H
-
-#include <stdint.h>
-
-#include <memory>
-#include <mutex>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include <unwindstack/Global.h>
-#include <unwindstack/Memory.h>
-
-namespace unwindstack {
-
-// Forward declarations.
-class DexFile;
-class Maps;
-struct MapInfo;
-enum ArchEnum : uint8_t;
-
-class DexFiles : public Global {
- public:
- explicit DexFiles(std::shared_ptr<Memory>& memory);
- DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
- virtual ~DexFiles();
-
- DexFile* GetDexFile(uint64_t dex_file_offset, MapInfo* info);
-
- void GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc, std::string* method_name,
- uint64_t* method_offset);
-
- private:
- void Init(Maps* maps);
-
- bool GetAddr(size_t index, uint64_t* addr);
-
- uint64_t ReadEntryPtr32(uint64_t addr);
-
- uint64_t ReadEntryPtr64(uint64_t addr);
-
- bool ReadEntry32();
-
- bool ReadEntry64();
-
- bool ReadVariableData(uint64_t ptr_offset) override;
-
- void ProcessArch() override;
-
- std::mutex lock_;
- bool initialized_ = false;
- std::unordered_map<uint64_t, std::unique_ptr<DexFile>> files_;
-
- uint64_t entry_addr_ = 0;
- uint64_t (DexFiles::*read_entry_ptr_func_)(uint64_t) = nullptr;
- bool (DexFiles::*read_entry_func_)() = nullptr;
- std::vector<uint64_t> addrs_;
-};
-
-} // namespace unwindstack
-
-#endif // _LIBUNWINDSTACK_DEX_FILES_H
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index ac94f10..25a665d 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -78,6 +78,8 @@
bool IsValidPc(uint64_t pc);
+ bool GetTextRange(uint64_t* addr, uint64_t* size);
+
void GetLastError(ErrorData* data);
ErrorCode GetLastErrorCode();
uint64_t GetLastErrorAddress();
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index dbd917d..945c277 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -68,6 +68,8 @@
virtual bool IsValidPc(uint64_t pc);
+ bool GetTextRange(uint64_t* addr, uint64_t* size);
+
Memory* CreateGnuDebugdataMemory();
Memory* memory() { return memory_; }
@@ -156,6 +158,9 @@
uint64_t gnu_build_id_offset_ = 0;
uint64_t gnu_build_id_size_ = 0;
+ uint64_t text_addr_ = 0;
+ uint64_t text_size_ = 0;
+
uint8_t soname_type_ = SONAME_UNKNOWN;
std::string soname_;
diff --git a/libunwindstack/include/unwindstack/JitDebug.h b/libunwindstack/include/unwindstack/JitDebug.h
index 8b7b4b5..0c3ded9 100644
--- a/libunwindstack/include/unwindstack/JitDebug.h
+++ b/libunwindstack/include/unwindstack/JitDebug.h
@@ -19,6 +19,7 @@
#include <stdint.h>
+#include <map>
#include <memory>
#include <mutex>
#include <string>
@@ -30,40 +31,24 @@
namespace unwindstack {
// Forward declarations.
-class Elf;
class Maps;
enum ArchEnum : uint8_t;
-class JitDebug : public Global {
+template <typename Symfile>
+class JitDebug {
public:
- explicit JitDebug(std::shared_ptr<Memory>& memory);
- JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
- virtual ~JitDebug();
+ static std::unique_ptr<JitDebug> Create(ArchEnum arch, std::shared_ptr<Memory>& memory,
+ std::vector<std::string> search_libs = {});
+ virtual ~JitDebug() {}
- Elf* GetElf(Maps* maps, uint64_t pc);
+ // Find symbol file for given pc.
+ virtual Symfile* Get(Maps* maps, uint64_t pc) = 0;
- private:
- void Init(Maps* maps);
-
- uint64_t (JitDebug::*read_descriptor_func_)(uint64_t) = nullptr;
- uint64_t (JitDebug::*read_entry_func_)(uint64_t*, uint64_t*) = nullptr;
-
- uint64_t ReadDescriptor32(uint64_t);
- uint64_t ReadDescriptor64(uint64_t);
-
- uint64_t ReadEntry32Pack(uint64_t* start, uint64_t* size);
- uint64_t ReadEntry32Pad(uint64_t* start, uint64_t* size);
- uint64_t ReadEntry64(uint64_t* start, uint64_t* size);
-
- bool ReadVariableData(uint64_t ptr_offset) override;
-
- void ProcessArch() override;
-
- uint64_t entry_addr_ = 0;
- bool initialized_ = false;
- std::vector<Elf*> elf_list_;
-
- std::mutex lock_;
+ // Find symbol for given pc.
+ bool GetFunctionName(Maps* maps, uint64_t pc, std::string* name, uint64_t* offset) {
+ Symfile* file = Get(maps, pc);
+ return file != nullptr && file->GetFunctionName(pc, name, offset);
+ }
};
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index 8b01654..a04d7c3 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -24,7 +24,6 @@
#include <string>
#include <vector>
-#include <unwindstack/DexFiles.h>
#include <unwindstack/Error.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
@@ -34,6 +33,7 @@
namespace unwindstack {
// Forward declarations.
+class DexFile;
class Elf;
enum ArchEnum : uint8_t;
@@ -63,14 +63,14 @@
class Unwinder {
public:
- Unwinder(size_t max_frames, Maps* maps, Regs* regs, std::shared_ptr<Memory> process_memory)
- : max_frames_(max_frames), maps_(maps), regs_(regs), process_memory_(process_memory) {
- frames_.reserve(max_frames);
- }
+ Unwinder(size_t max_frames, Maps* maps, Regs* regs, std::shared_ptr<Memory> process_memory);
Unwinder(size_t max_frames, Maps* maps, std::shared_ptr<Memory> process_memory)
- : max_frames_(max_frames), maps_(maps), process_memory_(process_memory) {
- frames_.reserve(max_frames);
- }
+ : Unwinder(max_frames, maps, nullptr, process_memory) {}
+
+ Unwinder(const Unwinder&) = delete;
+ Unwinder& operator=(const Unwinder&) = delete;
+ Unwinder(Unwinder&&) = default;
+ Unwinder& operator=(Unwinder&&) = default;
virtual ~Unwinder() = default;
@@ -90,9 +90,7 @@
std::string FormatFrame(size_t frame_num);
std::string FormatFrame(const FrameData& frame);
- void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
-
- void SetRegs(Regs* regs) { regs_ = regs; }
+ void SetRegs(Regs* regs);
Maps* GetMaps() { return maps_; }
std::shared_ptr<Memory>& GetProcessMemory() { return process_memory_; }
@@ -107,10 +105,6 @@
void SetDisplayBuildID(bool display_build_id) { display_build_id_ = display_build_id; }
-#if !defined(NO_LIBDEXFILE_SUPPORT)
- void SetDexFiles(DexFiles* dex_files, ArchEnum arch);
-#endif
-
ErrorCode LastErrorCode() { return last_error_.code; }
uint64_t LastErrorAddress() { return last_error_.address; }
@@ -126,9 +120,9 @@
Regs* regs_;
std::vector<FrameData> frames_;
std::shared_ptr<Memory> process_memory_;
- JitDebug* jit_debug_ = nullptr;
+ std::unique_ptr<JitDebug<Elf>> jit_debug_;
#if !defined(NO_LIBDEXFILE_SUPPORT)
- DexFiles* dex_files_ = nullptr;
+ std::unique_ptr<JitDebug<DexFile>> dex_files_;
#endif
bool resolve_names_ = true;
bool embedded_soname_ = true;
@@ -141,15 +135,11 @@
UnwinderFromPid(size_t max_frames, pid_t pid) : Unwinder(max_frames), pid_(pid) {}
virtual ~UnwinderFromPid() = default;
- bool Init(ArchEnum arch);
+ bool Init();
private:
pid_t pid_;
std::unique_ptr<Maps> maps_ptr_;
- std::unique_ptr<JitDebug> jit_debug_ptr_;
-#if !defined(NO_LIBDEXFILE_SUPPORT)
- std::unique_ptr<DexFiles> dex_files_ptr_;
-#endif
};
} // namespace unwindstack
diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp
index 0149a42..df7b31d 100644
--- a/libunwindstack/tests/DexFileTest.cpp
+++ b/libunwindstack/tests/DexFileTest.cpp
@@ -177,11 +177,11 @@
std::string method;
uint64_t method_offset;
- ASSERT_TRUE(dex_file->GetMethodInformation(0x102, &method, &method_offset));
+ ASSERT_TRUE(dex_file->GetFunctionName(0x4102, &method, &method_offset));
EXPECT_EQ("Main.<init>", method);
EXPECT_EQ(2U, method_offset);
- ASSERT_TRUE(dex_file->GetMethodInformation(0x118, &method, &method_offset));
+ ASSERT_TRUE(dex_file->GetFunctionName(0x4118, &method, &method_offset));
EXPECT_EQ("Main.main", method);
EXPECT_EQ(0U, method_offset);
}
@@ -195,9 +195,9 @@
std::string method;
uint64_t method_offset;
- EXPECT_FALSE(dex_file->GetMethodInformation(0x100000, &method, &method_offset));
+ EXPECT_FALSE(dex_file->GetFunctionName(0x100000, &method, &method_offset));
- EXPECT_FALSE(dex_file->GetMethodInformation(0x98, &method, &method_offset));
+ EXPECT_FALSE(dex_file->GetFunctionName(0x98, &method, &method_offset));
}
} // namespace unwindstack
diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp
index 1ea9e5c..655dcc8 100644
--- a/libunwindstack/tests/DexFilesTest.cpp
+++ b/libunwindstack/tests/DexFilesTest.cpp
@@ -22,8 +22,8 @@
#include <gtest/gtest.h>
-#include <unwindstack/DexFiles.h>
#include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
@@ -32,6 +32,10 @@
#include "ElfFake.h"
#include "MemoryFake.h"
+#if !defined(NO_LIBDEXFILE_SUPPORT)
+#include <DexFile.h>
+#endif
+
namespace unwindstack {
class DexFilesTest : public ::testing::Test {
@@ -48,8 +52,7 @@
}
void Init(ArchEnum arch) {
- dex_files_.reset(new DexFiles(process_memory_));
- dex_files_->SetArch(arch);
+ dex_files_ = JitDebug<DexFile>::Create(arch, process_memory_);
maps_.reset(
new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf\n"
@@ -86,10 +89,11 @@
Init(ARCH_ARM);
}
- void WriteDescriptor32(uint64_t addr, uint32_t head);
- void WriteDescriptor64(uint64_t addr, uint64_t head);
- void WriteEntry32(uint64_t entry_addr, uint32_t next, uint32_t prev, uint32_t dex_file);
- void WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev, uint64_t dex_file);
+ void WriteDescriptor32(uint64_t addr, uint32_t entry);
+ void WriteDescriptor64(uint64_t addr, uint64_t entry);
+ void WriteEntry32Pack(uint64_t addr, uint32_t next, uint32_t prev, uint32_t dex);
+ void WriteEntry32Pad(uint64_t addr, uint32_t next, uint32_t prev, uint32_t dex);
+ void WriteEntry64(uint64_t addr, uint64_t next, uint64_t prev, uint64_t dex);
void WriteDex(uint64_t dex_file);
static constexpr size_t kMapGlobalNonReadable = 2;
@@ -101,40 +105,70 @@
std::shared_ptr<Memory> process_memory_;
MemoryFake* memory_;
- std::unique_ptr<DexFiles> dex_files_;
+ std::unique_ptr<JitDebug<DexFile>> dex_files_;
std::unique_ptr<BufferMaps> maps_;
};
-void DexFilesTest::WriteDescriptor32(uint64_t addr, uint32_t head) {
- // void* first_entry_
- memory_->SetData32(addr + 12, head);
+void DexFilesTest::WriteDescriptor32(uint64_t addr, uint32_t entry) {
+ // Format of the 32 bit JITDescriptor structure:
+ // uint32_t version
+ memory_->SetData32(addr, 1);
+ // uint32_t action_flag
+ memory_->SetData32(addr + 4, 0);
+ // uint32_t relevant_entry
+ memory_->SetData32(addr + 8, 0);
+ // uint32_t first_entry
+ memory_->SetData32(addr + 12, entry);
}
-void DexFilesTest::WriteDescriptor64(uint64_t addr, uint64_t head) {
- // void* first_entry_
- memory_->SetData64(addr + 16, head);
+void DexFilesTest::WriteDescriptor64(uint64_t addr, uint64_t entry) {
+ // Format of the 64 bit JITDescriptor structure:
+ // uint32_t version
+ memory_->SetData32(addr, 1);
+ // uint32_t action_flag
+ memory_->SetData32(addr + 4, 0);
+ // uint64_t relevant_entry
+ memory_->SetData64(addr + 8, 0);
+ // uint64_t first_entry
+ memory_->SetData64(addr + 16, entry);
}
-void DexFilesTest::WriteEntry32(uint64_t entry_addr, uint32_t next, uint32_t prev,
- uint32_t dex_file) {
- // Format of the 32 bit DEXFileEntry structure:
+void DexFilesTest::WriteEntry32Pack(uint64_t addr, uint32_t next, uint32_t prev, uint32_t dex) {
+ // Format of the 32 bit JITCodeEntry structure:
// uint32_t next
- memory_->SetData32(entry_addr, next);
+ memory_->SetData32(addr, next);
// uint32_t prev
- memory_->SetData32(entry_addr + 4, prev);
- // uint32_t dex_file
- memory_->SetData32(entry_addr + 8, dex_file);
+ memory_->SetData32(addr + 4, prev);
+ // uint32_t dex
+ memory_->SetData32(addr + 8, dex);
+ // uint64_t symfile_size
+ memory_->SetData64(addr + 12, sizeof(kDexData) * sizeof(uint32_t));
}
-void DexFilesTest::WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev,
- uint64_t dex_file) {
- // Format of the 64 bit DEXFileEntry structure:
+void DexFilesTest::WriteEntry32Pad(uint64_t addr, uint32_t next, uint32_t prev, uint32_t dex) {
+ // Format of the 32 bit JITCodeEntry structure:
+ // uint32_t next
+ memory_->SetData32(addr, next);
+ // uint32_t prev
+ memory_->SetData32(addr + 4, prev);
+ // uint32_t dex
+ memory_->SetData32(addr + 8, dex);
+ // uint32_t pad
+ memory_->SetData32(addr + 12, 0);
+ // uint64_t symfile_size
+ memory_->SetData64(addr + 16, sizeof(kDexData) * sizeof(uint32_t));
+}
+
+void DexFilesTest::WriteEntry64(uint64_t addr, uint64_t next, uint64_t prev, uint64_t dex) {
+ // Format of the 64 bit JITCodeEntry structure:
// uint64_t next
- memory_->SetData64(entry_addr, next);
+ memory_->SetData64(addr, next);
// uint64_t prev
- memory_->SetData64(entry_addr + 8, prev);
- // uint64_t dex_file
- memory_->SetData64(entry_addr + 16, dex_file);
+ memory_->SetData64(addr + 8, prev);
+ // uint64_t dex
+ memory_->SetData64(addr + 16, dex);
+ // uint64_t symfile_size
+ memory_->SetData64(addr + 24, sizeof(kDexData) * sizeof(uint32_t));
}
void DexFilesTest::WriteDex(uint64_t dex_file) {
@@ -144,9 +178,8 @@
TEST_F(DexFilesTest, get_method_information_invalid) {
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
- MapInfo* info = maps_->Get(kMapDexFileEntries);
- dex_files_->GetMethodInformation(maps_.get(), info, 0, &method_name, &method_offset);
+ dex_files_->GetFunctionName(maps_.get(), 0, &method_name, &method_offset);
EXPECT_EQ("nothing", method_name);
EXPECT_EQ(0x124U, method_offset);
}
@@ -154,13 +187,12 @@
TEST_F(DexFilesTest, get_method_information_32) {
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
- MapInfo* info = maps_->Get(kMapDexFiles);
WriteDescriptor32(0xf800, 0x200000);
- WriteEntry32(0x200000, 0, 0, 0x300000);
+ WriteEntry32Pad(0x200000, 0, 0, 0x300000);
WriteDex(0x300000);
- dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ dex_files_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(0U, method_offset);
}
@@ -170,13 +202,12 @@
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
- MapInfo* info = maps_->Get(kMapDexFiles);
WriteDescriptor64(0xf800, 0x200000);
WriteEntry64(0x200000, 0, 0, 0x301000);
WriteDex(0x301000);
- dex_files_->GetMethodInformation(maps_.get(), info, 0x301102, &method_name, &method_offset);
+ dex_files_->GetFunctionName(maps_.get(), 0x301102, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(2U, method_offset);
}
@@ -184,14 +215,14 @@
TEST_F(DexFilesTest, get_method_information_not_first_entry_32) {
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
- MapInfo* info = maps_->Get(kMapDexFiles);
WriteDescriptor32(0xf800, 0x200000);
- WriteEntry32(0x200000, 0x200100, 0, 0x100000);
- WriteEntry32(0x200100, 0, 0x200000, 0x300000);
+ WriteEntry32Pad(0x200000, 0x200100, 0, 0x100000);
+ WriteDex(0x100000);
+ WriteEntry32Pad(0x200100, 0, 0x200000, 0x300000);
WriteDex(0x300000);
- dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
+ dex_files_->GetFunctionName(maps_.get(), 0x300104, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(4U, method_offset);
}
@@ -201,14 +232,14 @@
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
- MapInfo* info = maps_->Get(kMapDexFiles);
WriteDescriptor64(0xf800, 0x200000);
WriteEntry64(0x200000, 0x200100, 0, 0x100000);
+ WriteDex(0x100000);
WriteEntry64(0x200100, 0, 0x200000, 0x300000);
WriteDex(0x300000);
- dex_files_->GetMethodInformation(maps_.get(), info, 0x300106, &method_name, &method_offset);
+ dex_files_->GetFunctionName(maps_.get(), 0x300106, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(6U, method_offset);
}
@@ -216,19 +247,18 @@
TEST_F(DexFilesTest, get_method_information_cached) {
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
- MapInfo* info = maps_->Get(kMapDexFiles);
WriteDescriptor32(0xf800, 0x200000);
- WriteEntry32(0x200000, 0, 0, 0x300000);
+ WriteEntry32Pad(0x200000, 0, 0, 0x300000);
WriteDex(0x300000);
- dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ dex_files_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(0U, method_offset);
// Clear all memory and make sure that data is acquired from the cache.
memory_->Clear();
- dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ dex_files_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(0U, method_offset);
}
@@ -236,26 +266,24 @@
TEST_F(DexFilesTest, get_method_information_search_libs) {
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
- MapInfo* info = maps_->Get(kMapDexFiles);
WriteDescriptor32(0xf800, 0x200000);
- WriteEntry32(0x200000, 0x200100, 0, 0x100000);
- WriteEntry32(0x200100, 0, 0x200000, 0x300000);
+ WriteEntry32Pad(0x200000, 0x200100, 0, 0x100000);
+ WriteDex(0x100000);
+ WriteEntry32Pad(0x200100, 0, 0x200000, 0x300000);
WriteDex(0x300000);
// Only search a given named list of libs.
std::vector<std::string> libs{"libart.so"};
- dex_files_.reset(new DexFiles(process_memory_, libs));
- dex_files_->SetArch(ARCH_ARM);
+ dex_files_ = JitDebug<DexFile>::Create(ARCH_ARM, process_memory_, libs);
- dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
+ dex_files_->GetFunctionName(maps_.get(), 0x300104, &method_name, &method_offset);
EXPECT_EQ("nothing", method_name);
EXPECT_EQ(0x124U, method_offset);
MapInfo* map_info = maps_->Get(kMapGlobal);
map_info->name = "/system/lib/libart.so";
- dex_files_.reset(new DexFiles(process_memory_, libs));
- dex_files_->SetArch(ARCH_ARM);
+ dex_files_ = JitDebug<DexFile>::Create(ARCH_ARM, process_memory_, libs);
// Set the rw map to the same name or this will not scan this entry.
map_info = maps_->Get(kMapGlobalRw);
map_info->name = "/system/lib/libart.so";
@@ -263,7 +291,7 @@
// DexFiles object.
libs.clear();
- dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
+ dex_files_->GetFunctionName(maps_.get(), 0x300104, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(4U, method_offset);
}
@@ -271,26 +299,24 @@
TEST_F(DexFilesTest, get_method_information_global_skip_zero_32) {
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
- MapInfo* info = maps_->Get(kMapDexFiles);
// First global variable found, but value is zero.
WriteDescriptor32(0xa800, 0);
WriteDescriptor32(0xf800, 0x200000);
- WriteEntry32(0x200000, 0, 0, 0x300000);
+ WriteEntry32Pad(0x200000, 0, 0, 0x300000);
WriteDex(0x300000);
- dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ dex_files_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(0U, method_offset);
// Verify that second is ignored when first is set to non-zero
- dex_files_.reset(new DexFiles(process_memory_));
- dex_files_->SetArch(ARCH_ARM);
+ dex_files_ = JitDebug<DexFile>::Create(ARCH_ARM, process_memory_);
method_name = "fail";
method_offset = 0x123;
WriteDescriptor32(0xa800, 0x100000);
- dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ dex_files_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset);
EXPECT_EQ("fail", method_name);
EXPECT_EQ(0x123U, method_offset);
}
@@ -300,7 +326,6 @@
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
- MapInfo* info = maps_->Get(kMapDexFiles);
// First global variable found, but value is zero.
WriteDescriptor64(0xa800, 0);
@@ -309,17 +334,16 @@
WriteEntry64(0x200000, 0, 0, 0x300000);
WriteDex(0x300000);
- dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ dex_files_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(0U, method_offset);
// Verify that second is ignored when first is set to non-zero
- dex_files_.reset(new DexFiles(process_memory_));
- dex_files_->SetArch(ARCH_ARM64);
+ dex_files_ = JitDebug<DexFile>::Create(ARCH_ARM64, process_memory_);
method_name = "fail";
method_offset = 0x123;
WriteDescriptor64(0xa800, 0x100000);
- dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
+ dex_files_->GetFunctionName(maps_.get(), 0x300100, &method_name, &method_offset);
EXPECT_EQ("fail", method_name);
EXPECT_EQ(0x123U, method_offset);
}
diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp
index d620934..120bd73 100644
--- a/libunwindstack/tests/DwarfDebugFrameTest.cpp
+++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp
@@ -550,6 +550,22 @@
VerifyCieVersion(cie, 4, 10, DW_EH_PE_sdata8, 0x181, 0x1c, 0x10c);
}
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset32_version5) {
+ SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{5, '\0', 0, 10, 4, 8, 0x81, 3});
+ const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieVersion(cie, 5, 10, DW_EH_PE_sdata4, 0x181, 0x10, 0x104);
+}
+
+TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset64_version5) {
+ SetCie64(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{5, '\0', 0, 10, 4, 8, 0x81, 3});
+ const DwarfCie* cie = this->debug_frame_->GetCieFromOffset(0x5000);
+ EXPECT_EQ(DWARF_ERROR_NONE, this->debug_frame_->LastErrorCode());
+ ASSERT_TRUE(cie != nullptr);
+ VerifyCieVersion(cie, 5, 10, DW_EH_PE_sdata8, 0x181, 0x1c, 0x10c);
+}
+
TYPED_TEST_P(DwarfDebugFrameTest, GetCieFromOffset_version_invalid) {
SetCie32(&this->memory_, 0x5000, 0x100, std::vector<uint8_t>{0, '\0', 1, 2, 3, 4, 5, 6, 7});
ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x5000) == nullptr);
@@ -558,10 +574,10 @@
ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x6000) == nullptr);
EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
- SetCie32(&this->memory_, 0x7000, 0x100, std::vector<uint8_t>{5, '\0', 1, 2, 3, 4, 5, 6, 7});
+ SetCie32(&this->memory_, 0x7000, 0x100, std::vector<uint8_t>{6, '\0', 1, 2, 3, 4, 5, 6, 7});
ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x7000) == nullptr);
EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
- SetCie64(&this->memory_, 0x8000, 0x100, std::vector<uint8_t>{5, '\0', 1, 2, 3, 4, 5, 6, 7});
+ SetCie64(&this->memory_, 0x8000, 0x100, std::vector<uint8_t>{6, '\0', 1, 2, 3, 4, 5, 6, 7});
ASSERT_TRUE(this->debug_frame_->GetCieFromOffset(0x8000) == nullptr);
EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->debug_frame_->LastErrorCode());
}
@@ -803,9 +819,10 @@
GetFdeFromPc64_not_in_section, GetCieFde32, GetCieFde64, GetCieFromOffset32_cie_cached,
GetCieFromOffset64_cie_cached, GetCieFromOffset32_version1, GetCieFromOffset64_version1,
GetCieFromOffset32_version3, GetCieFromOffset64_version3, GetCieFromOffset32_version4,
- GetCieFromOffset64_version4, GetCieFromOffset_version_invalid, GetCieFromOffset32_augment,
- GetCieFromOffset64_augment, GetFdeFromOffset32_augment, GetFdeFromOffset64_augment,
- GetFdeFromOffset32_lsda_address, GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved);
+ GetCieFromOffset64_version4, GetCieFromOffset32_version5, GetCieFromOffset64_version5,
+ GetCieFromOffset_version_invalid, GetCieFromOffset32_augment, GetCieFromOffset64_augment,
+ GetFdeFromOffset32_augment, GetFdeFromOffset64_augment, GetFdeFromOffset32_lsda_address,
+ GetFdeFromOffset64_lsda_address, GetFdeFromPc_interleaved);
typedef ::testing::Types<uint32_t, uint64_t> DwarfDebugFrameTestTypes;
INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes);
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index d895863..cdc927a 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -1192,14 +1192,16 @@
char note_section[128];
Nhdr note_header = {};
note_header.n_namesz = 4; // "GNU"
- note_header.n_descsz = 8; // "BUILDID"
+ note_header.n_descsz = 7; // "BUILDID"
note_header.n_type = NT_GNU_BUILD_ID;
memcpy(¬e_section, ¬e_header, sizeof(note_header));
size_t note_offset = sizeof(note_header);
+ // The note information contains the GNU and trailing '\0'.
memcpy(¬e_section[note_offset], "GNU", sizeof("GNU"));
note_offset += sizeof("GNU");
- memcpy(¬e_section[note_offset], "BUILDID", sizeof("BUILDID"));
- note_offset += sizeof("BUILDID");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(¬e_section[note_offset], "BUILDID", 7);
+ note_offset += 8;
Shdr shdr = {};
shdr.sh_type = SHT_NOTE;
@@ -1244,24 +1246,27 @@
char note_section[128];
Nhdr note_header = {};
note_header.n_namesz = 8; // "WRONG" aligned to 4
- note_header.n_descsz = 8; // "BUILDID"
+ note_header.n_descsz = 7; // "BUILDID"
note_header.n_type = NT_GNU_BUILD_ID;
memcpy(¬e_section, ¬e_header, sizeof(note_header));
size_t note_offset = sizeof(note_header);
memcpy(¬e_section[note_offset], "WRONG", sizeof("WRONG"));
note_offset += 8;
- memcpy(¬e_section[note_offset], "BUILDID", sizeof("BUILDID"));
- note_offset += sizeof("BUILDID");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(¬e_section[note_offset], "BUILDID", 7);
+ note_offset += 8;
note_header.n_namesz = 4; // "GNU"
- note_header.n_descsz = 8; // "BUILDID"
+ note_header.n_descsz = 7; // "BUILDID"
note_header.n_type = NT_GNU_BUILD_ID;
memcpy(¬e_section[note_offset], ¬e_header, sizeof(note_header));
note_offset += sizeof(note_header);
+ // The note information contains the GNU and trailing '\0'.
memcpy(¬e_section[note_offset], "GNU", sizeof("GNU"));
note_offset += sizeof("GNU");
- memcpy(¬e_section[note_offset], "BUILDID", sizeof("BUILDID"));
- note_offset += sizeof("BUILDID");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(¬e_section[note_offset], "BUILDID", 7);
+ note_offset += 8;
Shdr shdr = {};
shdr.sh_type = SHT_NOTE;
@@ -1306,14 +1311,16 @@
char note_section[128];
Nhdr note_header = {};
note_header.n_namesz = 4; // "GNU"
- note_header.n_descsz = 8; // "BUILDID"
+ note_header.n_descsz = 7; // "BUILDID"
note_header.n_type = NT_GNU_BUILD_ID;
memcpy(¬e_section, ¬e_header, sizeof(note_header));
size_t note_offset = sizeof(note_header);
+ // The note information contains the GNU and trailing '\0'.
memcpy(¬e_section[note_offset], "GNU", sizeof("GNU"));
note_offset += sizeof("GNU");
- memcpy(¬e_section[note_offset], "BUILDID", sizeof("BUILDID"));
- note_offset += sizeof("BUILDID");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(¬e_section[note_offset], "BUILDID", 7);
+ note_offset += 8;
Shdr shdr = {};
shdr.sh_type = SHT_NOTE;
@@ -1358,14 +1365,16 @@
char note_section[128];
Nhdr note_header = {};
note_header.n_namesz = 4; // "GNU"
- note_header.n_descsz = 8; // "BUILDID"
+ note_header.n_descsz = 7; // "BUILDID"
note_header.n_type = NT_GNU_BUILD_ID;
memcpy(¬e_section, ¬e_header, sizeof(note_header));
size_t note_offset = sizeof(note_header);
+ // The note information contains the GNU and trailing '\0'.
memcpy(¬e_section[note_offset], "GNU", sizeof("GNU"));
note_offset += sizeof("GNU");
- memcpy(¬e_section[note_offset], "BUILDID", sizeof("BUILDID"));
- note_offset += sizeof("BUILDID");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(¬e_section[note_offset], "BUILDID", 7);
+ note_offset += 8;
Shdr shdr = {};
shdr.sh_type = SHT_NOTE;
@@ -1410,14 +1419,16 @@
char note_section[128];
Nhdr note_header = {};
note_header.n_namesz = 4; // "GNU"
- note_header.n_descsz = 8; // "BUILDID"
+ note_header.n_descsz = 7; // "BUILDID"
note_header.n_type = NT_GNU_BUILD_ID;
memcpy(¬e_section, ¬e_header, sizeof(note_header));
size_t note_offset = sizeof(note_header);
+ // The note information contains the GNU and trailing '\0'.
memcpy(¬e_section[note_offset], "GNU", sizeof("GNU"));
note_offset += sizeof("GNU");
- memcpy(¬e_section[note_offset], "BUILDID", sizeof("BUILDID"));
- note_offset += sizeof("BUILDID");
+ // This part of the note does not contain any trailing '\0'.
+ memcpy(¬e_section[note_offset], "BUILDID", 7);
+ note_offset += 8;
Shdr shdr = {};
shdr.sh_type = SHT_NOTE;
diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp
index b1ca111..438194a 100644
--- a/libunwindstack/tests/JitDebugTest.cpp
+++ b/libunwindstack/tests/JitDebugTest.cpp
@@ -46,8 +46,7 @@
}
void Init(ArchEnum arch) {
- jit_debug_.reset(new JitDebug(process_memory_));
- jit_debug_->SetArch(arch);
+ jit_debug_ = JitDebug<Elf>::Create(arch, process_memory_);
maps_.reset(
new BufferMaps("1000-4000 ---s 00000000 00:00 0 /fake/elf1\n"
@@ -62,6 +61,12 @@
"200000-210000 rw-p 0002000 00:00 0 /fake/elf4\n"));
ASSERT_TRUE(maps_->Parse());
+ // Ensure all memory of the ELF file is initialized,
+ // otherwise reads within it may fail.
+ for (uint64_t addr = 0x4000; addr < 0x6000; addr += 8) {
+ memory_->SetData64(addr, 0);
+ }
+
MapInfo* map_info = maps_->Get(3);
ASSERT_TRUE(map_info != nullptr);
CreateFakeElf(map_info);
@@ -94,7 +99,7 @@
ehdr.e_shstrndx = 1;
ehdr.e_shoff = sh_offset;
ehdr.e_shentsize = sizeof(ShdrType);
- ehdr.e_shnum = 3;
+ ehdr.e_shnum = 4;
memory_->SetMemory(offset, &ehdr, sizeof(ehdr));
ShdrType shdr;
@@ -110,6 +115,7 @@
shdr.sh_size = 0x100;
memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
memory_->SetMemory(offset + 0x500, ".debug_frame");
+ memory_->SetMemory(offset + 0x550, ".text");
sh_offset += sizeof(shdr);
memset(&shdr, 0, sizeof(shdr));
@@ -120,6 +126,15 @@
shdr.sh_size = 0x200;
memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_NOBITS;
+ shdr.sh_name = 0x50;
+ shdr.sh_addr = pc;
+ shdr.sh_offset = 0;
+ shdr.sh_size = size;
+ memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+
// Now add a single cie/fde.
uint64_t dwarf_offset = offset + 0x600;
if (class_type == ELFCLASS32) {
@@ -168,7 +183,7 @@
std::shared_ptr<Memory> process_memory_;
MemoryFake* memory_;
- std::unique_ptr<JitDebug> jit_debug_;
+ std::unique_ptr<JitDebug<Elf>> jit_debug_;
std::unique_ptr<BufferMaps> maps_;
};
@@ -238,20 +253,20 @@
}
TEST_F(JitDebugTest, get_elf_invalid) {
- Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ Elf* elf = jit_debug_->Get(maps_.get(), 0x1500);
ASSERT_TRUE(elf == nullptr);
}
TEST_F(JitDebugTest, get_elf_no_global_variable) {
maps_.reset(new BufferMaps(""));
- Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ Elf* elf = jit_debug_->Get(maps_.get(), 0x1500);
ASSERT_TRUE(elf == nullptr);
}
TEST_F(JitDebugTest, get_elf_no_valid_descriptor_in_memory) {
CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
- Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ Elf* elf = jit_debug_->Get(maps_.get(), 0x1500);
ASSERT_TRUE(elf == nullptr);
}
@@ -260,7 +275,7 @@
WriteDescriptor32(0xf800, 0x200000);
- Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ Elf* elf = jit_debug_->Get(maps_.get(), 0x1500);
ASSERT_TRUE(elf == nullptr);
}
@@ -269,7 +284,7 @@
WriteDescriptor32(0xf800, 0);
- Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ Elf* elf = jit_debug_->Get(maps_.get(), 0x1500);
ASSERT_TRUE(elf == nullptr);
}
@@ -280,7 +295,7 @@
// Set the version to an invalid value.
memory_->SetData32(0xf800, 2);
- Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ Elf* elf = jit_debug_->Get(maps_.get(), 0x1500);
ASSERT_TRUE(elf == nullptr);
}
@@ -290,12 +305,18 @@
WriteDescriptor32(0xf800, 0x200000);
WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000);
- Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ Elf* elf = jit_debug_->Get(maps_.get(), 0x1500);
ASSERT_TRUE(elf != nullptr);
+ uint64_t text_addr;
+ uint64_t text_size;
+ ASSERT_TRUE(elf->GetTextRange(&text_addr, &text_size));
+ ASSERT_EQ(text_addr, 0x1500u);
+ ASSERT_EQ(text_size, 0x200u);
// Clear the memory and verify all of the data is cached.
memory_->Clear();
- Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
+ WriteDescriptor32(0xf800, 0x200000);
+ Elf* elf2 = jit_debug_->Get(maps_.get(), 0x1500);
ASSERT_TRUE(elf2 != nullptr);
EXPECT_EQ(elf, elf2);
}
@@ -309,16 +330,15 @@
WriteDescriptor32(0x12800, 0x201000);
WriteEntry32Pad(0x201000, 0, 0, 0x5000, 0x1000);
- ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) != nullptr);
- ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x2000) == nullptr);
+ ASSERT_TRUE(jit_debug_->Get(maps_.get(), 0x1500) != nullptr);
+ ASSERT_TRUE(jit_debug_->Get(maps_.get(), 0x2000) == nullptr);
// Now clear the descriptor entry for the first one.
WriteDescriptor32(0xf800, 0);
- jit_debug_.reset(new JitDebug(process_memory_));
- jit_debug_->SetArch(ARCH_ARM);
+ jit_debug_ = JitDebug<Elf>::Create(ARCH_ARM, process_memory_);
- ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) == nullptr);
- ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x2000) != nullptr);
+ ASSERT_TRUE(jit_debug_->Get(maps_.get(), 0x1500) == nullptr);
+ ASSERT_TRUE(jit_debug_->Get(maps_.get(), 0x2000) != nullptr);
}
TEST_F(JitDebugTest, get_elf_x86) {
@@ -329,13 +349,14 @@
WriteDescriptor32(0xf800, 0x200000);
WriteEntry32Pack(0x200000, 0, 0, 0x4000, 0x1000);
- jit_debug_->SetArch(ARCH_X86);
- Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ jit_debug_ = JitDebug<Elf>::Create(ARCH_X86, process_memory_);
+ Elf* elf = jit_debug_->Get(maps_.get(), 0x1500);
ASSERT_TRUE(elf != nullptr);
// Clear the memory and verify all of the data is cached.
memory_->Clear();
- Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
+ WriteDescriptor32(0xf800, 0x200000);
+ Elf* elf2 = jit_debug_->Get(maps_.get(), 0x1500);
ASSERT_TRUE(elf2 != nullptr);
EXPECT_EQ(elf, elf2);
}
@@ -348,12 +369,13 @@
WriteDescriptor64(0xf800, 0x200000);
WriteEntry64(0x200000, 0, 0, 0x4000, 0x1000);
- Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ Elf* elf = jit_debug_->Get(maps_.get(), 0x1500);
ASSERT_TRUE(elf != nullptr);
// Clear the memory and verify all of the data is cached.
memory_->Clear();
- Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
+ WriteDescriptor64(0xf800, 0x200000);
+ Elf* elf2 = jit_debug_->Get(maps_.get(), 0x1500);
ASSERT_TRUE(elf2 != nullptr);
EXPECT_EQ(elf, elf2);
}
@@ -366,20 +388,21 @@
WriteEntry32Pad(0x200000, 0, 0x200100, 0x4000, 0x1000);
WriteEntry32Pad(0x200100, 0x200100, 0, 0x5000, 0x1000);
- Elf* elf_2 = jit_debug_->GetElf(maps_.get(), 0x2400);
+ Elf* elf_2 = jit_debug_->Get(maps_.get(), 0x2400);
ASSERT_TRUE(elf_2 != nullptr);
- Elf* elf_1 = jit_debug_->GetElf(maps_.get(), 0x1600);
+ Elf* elf_1 = jit_debug_->Get(maps_.get(), 0x1600);
ASSERT_TRUE(elf_1 != nullptr);
// Clear the memory and verify all of the data is cached.
memory_->Clear();
- EXPECT_EQ(elf_1, jit_debug_->GetElf(maps_.get(), 0x1500));
- EXPECT_EQ(elf_1, jit_debug_->GetElf(maps_.get(), 0x16ff));
- EXPECT_EQ(elf_2, jit_debug_->GetElf(maps_.get(), 0x2300));
- EXPECT_EQ(elf_2, jit_debug_->GetElf(maps_.get(), 0x26ff));
- EXPECT_EQ(nullptr, jit_debug_->GetElf(maps_.get(), 0x1700));
- EXPECT_EQ(nullptr, jit_debug_->GetElf(maps_.get(), 0x2700));
+ WriteDescriptor32(0xf800, 0x200000);
+ EXPECT_EQ(elf_1, jit_debug_->Get(maps_.get(), 0x1500));
+ EXPECT_EQ(elf_1, jit_debug_->Get(maps_.get(), 0x16ff));
+ EXPECT_EQ(elf_2, jit_debug_->Get(maps_.get(), 0x2300));
+ EXPECT_EQ(elf_2, jit_debug_->Get(maps_.get(), 0x26ff));
+ EXPECT_EQ(nullptr, jit_debug_->Get(maps_.get(), 0x1700));
+ EXPECT_EQ(nullptr, jit_debug_->Get(maps_.get(), 0x2700));
}
TEST_F(JitDebugTest, get_elf_search_libs) {
@@ -390,21 +413,19 @@
// Only search a given named list of libs.
std::vector<std::string> libs{"libart.so"};
- jit_debug_.reset(new JitDebug(process_memory_, libs));
- jit_debug_->SetArch(ARCH_ARM);
- EXPECT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) == nullptr);
+ jit_debug_ = JitDebug<Elf>::Create(ARCH_ARM, process_memory_, libs);
+ EXPECT_TRUE(jit_debug_->Get(maps_.get(), 0x1500) == nullptr);
// Change the name of the map that includes the value and verify this works.
MapInfo* map_info = maps_->Get(5);
map_info->name = "/system/lib/libart.so";
map_info = maps_->Get(6);
map_info->name = "/system/lib/libart.so";
- jit_debug_.reset(new JitDebug(process_memory_, libs));
+ jit_debug_ = JitDebug<Elf>::Create(ARCH_ARM, process_memory_);
// Make sure that clearing our copy of the libs doesn't affect the
// JitDebug object.
libs.clear();
- jit_debug_->SetArch(ARCH_ARM);
- EXPECT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) != nullptr);
+ EXPECT_TRUE(jit_debug_->Get(maps_.get(), 0x1500) != nullptr);
}
} // namespace unwindstack
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 655579e..e3c646a 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -204,6 +204,7 @@
TEST_F(UnwindOfflineTest, pc_straddle_arm) {
ASSERT_NO_FATAL_FAILURE(Init("straddle_arm/", ARCH_ARM));
+ std::unique_ptr<Regs> regs_copy(regs_->Clone());
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
unwinder.Unwind();
@@ -223,6 +224,22 @@
EXPECT_EQ(0xe9c86730U, unwinder.frames()[2].sp);
EXPECT_EQ(0xf3367147U, unwinder.frames()[3].pc);
EXPECT_EQ(0xe9c86778U, unwinder.frames()[3].sp);
+
+ // Display build ids now.
+ unwinder.SetRegs(regs_copy.get());
+ unwinder.SetDisplayBuildID(true);
+ unwinder.Unwind();
+
+ frame_info = DumpFrames(unwinder);
+ ASSERT_EQ(4U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 0001a9f8 libc.so (abort+64) (BuildId: 2dd0d4ba881322a0edabeed94808048c)\n"
+ " #01 pc 00006a1b libbase.so (android::base::DefaultAborter(char const*)+6) (BuildId: "
+ "ed43842c239cac1a618e600ea91c4cbd)\n"
+ " #02 pc 00007441 libbase.so (android::base::LogMessage::~LogMessage()+748) (BuildId: "
+ "ed43842c239cac1a618e600ea91c4cbd)\n"
+ " #03 pc 00015147 /does/not/exist/libhidlbase.so\n",
+ frame_info);
}
TEST_F(UnwindOfflineTest, pc_in_gnu_debugdata_arm) {
@@ -290,9 +307,7 @@
}
process_memory_.reset(memory);
- JitDebug jit_debug(process_memory_);
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
- unwinder.SetJitDebug(&jit_debug, regs_->Arch());
unwinder.Unwind();
std::string frame_info(DumpFrames(unwinder));
@@ -592,9 +607,7 @@
}
process_memory_.reset(memory);
- JitDebug jit_debug(process_memory_);
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
- unwinder.SetJitDebug(&jit_debug, regs_->Arch());
unwinder.Unwind();
std::string frame_info(DumpFrames(unwinder));
@@ -915,9 +928,7 @@
LeakType* leak_data = reinterpret_cast<LeakType*>(data);
std::unique_ptr<Regs> regs_copy(leak_data->regs->Clone());
- JitDebug jit_debug(leak_data->process_memory);
Unwinder unwinder(128, leak_data->maps, regs_copy.get(), leak_data->process_memory);
- unwinder.SetJitDebug(&jit_debug, regs_copy->Arch());
unwinder.Unwind();
ASSERT_EQ(76U, unwinder.NumFrames());
}
@@ -1038,9 +1049,7 @@
}
process_memory_.reset(memory);
- JitDebug jit_debug(process_memory_);
Unwinder unwinder(128, maps_.get(), regs_.get(), process_memory_);
- unwinder.SetJitDebug(&jit_debug, regs_->Arch());
unwinder.Unwind();
std::string frame_info(DumpFrames(unwinder));
diff --git a/libunwindstack/tests/UnwindTest.cpp b/libunwindstack/tests/UnwindTest.cpp
index 4e38015..5e7e6bf 100644
--- a/libunwindstack/tests/UnwindTest.cpp
+++ b/libunwindstack/tests/UnwindTest.cpp
@@ -170,7 +170,7 @@
unwinder.reset(new Unwinder(512, maps.get(), regs.get(), process_memory));
} else {
UnwinderFromPid* unwinder_from_pid = new UnwinderFromPid(512, getpid());
- ASSERT_TRUE(unwinder_from_pid->Init(regs->Arch()));
+ ASSERT_TRUE(unwinder_from_pid->Init());
unwinder_from_pid->SetRegs(regs.get());
unwinder.reset(unwinder_from_pid);
}
@@ -283,7 +283,7 @@
ASSERT_TRUE(regs.get() != nullptr);
UnwinderFromPid unwinder(512, pid);
- ASSERT_TRUE(unwinder.Init(regs->Arch()));
+ ASSERT_TRUE(unwinder.Init());
unwinder.SetRegs(regs.get());
VerifyUnwind(&unwinder, kFunctionOrder);
@@ -335,7 +335,7 @@
ASSERT_TRUE(regs.get() != nullptr);
UnwinderFromPid unwinder(512, *pid);
- ASSERT_TRUE(unwinder.Init(regs->Arch()));
+ ASSERT_TRUE(unwinder.Init());
unwinder.SetRegs(regs.get());
VerifyUnwind(&unwinder, kFunctionOrder);
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
index 1812e50..cad95f8 100644
--- a/libunwindstack/tools/unwind.cpp
+++ b/libunwindstack/tools/unwind.cpp
@@ -26,7 +26,6 @@
#include <sys/types.h>
#include <unistd.h>
-#include <unwindstack/DexFiles.h>
#include <unwindstack/Elf.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
@@ -90,7 +89,7 @@
printf("\n");
unwindstack::UnwinderFromPid unwinder(1024, pid);
- if (!unwinder.Init(regs->Arch())) {
+ if (!unwinder.Init()) {
printf("Failed to init unwinder object.\n");
return;
}
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index 4f67d67..86f3163 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -248,7 +248,7 @@
// Do an unwind so we know how much of the stack to save, and what
// elf files are involved.
unwindstack::UnwinderFromPid unwinder(1024, pid);
- if (!unwinder.Init(regs->Arch())) {
+ if (!unwinder.Init()) {
printf("Unable to init unwinder object.\n");
return 1;
}
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 3e7d0ba..4f194c7 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -183,6 +183,9 @@
"ProcessCallStack.cpp",
],
},
+ darwin: {
+ enabled: false,
+ },
windows: {
enabled: false,
},
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index b3f943d..2d696eb 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -76,8 +76,8 @@
}
void Looper::initTLSKey() {
- int result = pthread_key_create(& gTLSKey, threadDestructor);
- LOG_ALWAYS_FATAL_IF(result != 0, "Could not allocate TLS key.");
+ int error = pthread_key_create(&gTLSKey, threadDestructor);
+ LOG_ALWAYS_FATAL_IF(error != 0, "Could not allocate TLS key: %s", strerror(error));
}
void Looper::threadDestructor(void *st) {
@@ -399,8 +399,8 @@
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));
if (nWrite != sizeof(uint64_t)) {
if (errno != EAGAIN) {
- LOG_ALWAYS_FATAL("Could not write wake signal to fd %d: %s", mWakeEventFd.get(),
- strerror(errno));
+ LOG_ALWAYS_FATAL("Could not write wake signal to fd %d (returned %zd): %s",
+ mWakeEventFd.get(), nWrite, strerror(errno));
}
}
}
diff --git a/libvndksupport/Android.bp b/libvndksupport/Android.bp
index bfa2508..546c15c 100644
--- a/libvndksupport/Android.bp
+++ b/libvndksupport/Android.bp
@@ -9,7 +9,10 @@
],
local_include_dirs: ["include/vndksupport"],
export_include_dirs: ["include"],
- shared_libs: ["liblog"],
+ shared_libs: [
+ "libdl_android",
+ "liblog",
+ ],
version_script: "libvndksupport.map.txt",
stubs: {
symbol_file: "libvndksupport.map.txt",
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 0710d0a..96dbba1 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -102,21 +102,9 @@
}
static uint32_t ComputeHash(const ZipString& name) {
-#if !defined(_WIN32)
return std::hash<std::string_view>{}(
- std::string_view(reinterpret_cast<const char*>(name.name), name.name_length));
-#else
- // Remove this code path once the windows compiler knows how to compile the above statement.
- uint32_t hash = 0;
- uint16_t len = name.name_length;
- const uint8_t* str = name.name;
-
- while (len--) {
- hash = hash * 31 + *str++;
- }
-
- return hash;
-#endif
+ std::string_view(reinterpret_cast<const char*>(name.name), name.name_length)) &
+ UINT32_MAX;
}
static bool isZipStringEqual(const uint8_t* start, const ZipString& zip_string,
diff --git a/rootdir/etc/ld.config.legacy.txt b/rootdir/etc/ld.config.legacy.txt
index 0fccd31..7324ba9 100644
--- a/rootdir/etc/ld.config.legacy.txt
+++ b/rootdir/etc/ld.config.legacy.txt
@@ -100,6 +100,7 @@
namespace.media.link.default.shared_libs = libandroid.so
namespace.media.link.default.shared_libs += libbinder_ndk.so
namespace.media.link.default.shared_libs += libc.so
+namespace.media.link.default.shared_libs += libcgrouprc.so
namespace.media.link.default.shared_libs += libdl.so
namespace.media.link.default.shared_libs += liblog.so
namespace.media.link.default.shared_libs += libmediametrics.so
@@ -143,6 +144,7 @@
namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
namespace.resolv.links = default
namespace.resolv.link.default.shared_libs = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
namespace.resolv.link.default.shared_libs += libm.so
namespace.resolv.link.default.shared_libs += libdl.so
namespace.resolv.link.default.shared_libs += libbinder_ndk.so
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index 2b0ef4c..45e80e1 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -212,6 +212,7 @@
namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
namespace.resolv.links = default
namespace.resolv.link.default.shared_libs = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
namespace.resolv.link.default.shared_libs += libm.so
namespace.resolv.link.default.shared_libs += libdl.so
namespace.resolv.link.default.shared_libs += libbinder_ndk.so
@@ -253,17 +254,19 @@
namespace.sphal.asan.permitted.paths += /vendor/${LIB}
# Once in this namespace, access to libraries in /system/lib is restricted. Only
-# libs listed here can be used.
-namespace.sphal.links = default,vndk,rs
+# libs listed here can be used. Order is important here as the namespaces are
+# tried in this order. rs should be before vndk because both are capable
+# of loading libRS_internal.so
+namespace.sphal.links = rs,default,vndk
+
+# Renderscript gets separate namespace
+namespace.sphal.link.rs.shared_libs = libRS_internal.so
namespace.sphal.link.default.shared_libs = %LLNDK_LIBRARIES%
namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
namespace.sphal.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
-# Renderscript gets separate namespace
-namespace.sphal.link.rs.shared_libs = libRS_internal.so
-
###############################################################################
# "rs" namespace
#
diff --git a/rootdir/etc/ld.config.vndk_lite.txt b/rootdir/etc/ld.config.vndk_lite.txt
index c8312df..a762ba8 100644
--- a/rootdir/etc/ld.config.vndk_lite.txt
+++ b/rootdir/etc/ld.config.vndk_lite.txt
@@ -154,6 +154,7 @@
namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
namespace.resolv.links = default
namespace.resolv.link.default.shared_libs = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
namespace.resolv.link.default.shared_libs += libm.so
namespace.resolv.link.default.shared_libs += libdl.so
namespace.resolv.link.default.shared_libs += libbinder_ndk.so
@@ -195,17 +196,19 @@
namespace.sphal.asan.permitted.paths += /vendor/${LIB}
# Once in this namespace, access to libraries in /system/lib is restricted. Only
-# libs listed here can be used.
-namespace.sphal.links = default,vndk,rs
+# libs listed here can be used. Order is important here as the namespaces are
+# tried in this order. rs should be before vndk because both are capable
+# of loading libRS_internal.so
+namespace.sphal.links = rs,default,vndk
+
+# Renderscript gets separate namespace
+namespace.sphal.link.rs.shared_libs = libRS_internal.so
namespace.sphal.link.default.shared_libs = %LLNDK_LIBRARIES%
namespace.sphal.link.default.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
namespace.sphal.link.vndk.shared_libs = %VNDK_SAMEPROCESS_LIBRARIES%
-# Renderscript gets separate namespace
-namespace.sphal.link.rs.shared_libs = libRS_internal.so
-
###############################################################################
# "rs" namespace
#
@@ -474,6 +477,7 @@
namespace.resolv.asan.search.paths = /apex/com.android.resolv/${LIB}
namespace.resolv.links = default
namespace.resolv.link.default.shared_libs = libc.so
+namespace.resolv.link.default.shared_libs += libcgrouprc.so
namespace.resolv.link.default.shared_libs += libm.so
namespace.resolv.link.default.shared_libs += libdl.so
namespace.resolv.link.default.shared_libs += libbinder_ndk.so
diff --git a/rootdir/fsverity_init.sh b/rootdir/fsverity_init.sh
index 29e4519..4fee15f 100644
--- a/rootdir/fsverity_init.sh
+++ b/rootdir/fsverity_init.sh
@@ -24,6 +24,9 @@
log -p e -t fsverity_init "Failed to load $cert"
done
-# Prevent future key links to .fs-verity keyring
-/system/bin/mini-keyctl restrict_keyring .fs-verity ||
- log -p e -t fsverity_init "Failed to restrict .fs-verity keyring"
+DEBUGGABLE=$(getprop ro.debuggable)
+if [ $DEBUGGABLE != "1" ]; then
+ # Prevent future key links to .fs-verity keyring
+ /system/bin/mini-keyctl restrict_keyring .fs-verity ||
+ log -p e -t fsverity_init "Failed to restrict .fs-verity keyring"
+fi
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 1f852ff..5289976 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -56,14 +56,6 @@
defaults: ["toolbox_binary_defaults"],
}
-// We only want 'r' on userdebug and eng builds.
-cc_binary {
- name: "r",
- defaults: ["toolbox_defaults"],
- srcs: ["r.c"],
- vendor_available: true,
-}
-
// We build BSD grep separately (but see http://b/111849261).
cc_defaults {
name: "grep_common",
diff --git a/toolbox/r.c b/toolbox/r.c
deleted file mode 100644
index b96cdb2..0000000
--- a/toolbox/r.c
+++ /dev/null
@@ -1,102 +0,0 @@
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <unistd.h>
-
-#if __LP64__
-#define strtoptr strtoull
-#else
-#define strtoptr strtoul
-#endif
-
-static int usage()
-{
- fprintf(stderr,"r [-b|-s] <address> [<value>]\n");
- return -1;
-}
-
-int main(int argc, char *argv[])
-{
- if(argc < 2) return usage();
-
- int width = 4;
- if(!strcmp(argv[1], "-b")) {
- width = 1;
- argc--;
- argv++;
- } else if(!strcmp(argv[1], "-s")) {
- width = 2;
- argc--;
- argv++;
- }
-
- if(argc < 2) return usage();
- uintptr_t addr = strtoptr(argv[1], 0, 16);
-
- uintptr_t endaddr = 0;
- char* end = strchr(argv[1], '-');
- if (end)
- endaddr = strtoptr(end + 1, 0, 16);
-
- if (!endaddr)
- endaddr = addr + width - 1;
-
- if (endaddr <= addr) {
- fprintf(stderr, "end address <= start address\n");
- return -1;
- }
-
- bool set = false;
- uint32_t value = 0;
- if(argc > 2) {
- set = true;
- value = strtoul(argv[2], 0, 16);
- }
-
- int fd = open("/dev/mem", O_RDWR | O_SYNC);
- if(fd < 0) {
- fprintf(stderr,"cannot open /dev/mem\n");
- return -1;
- }
-
- off64_t mmap_start = addr & ~(PAGE_SIZE - 1);
- size_t mmap_size = endaddr - mmap_start + 1;
- mmap_size = (mmap_size + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
-
- void* page = mmap64(0, mmap_size, PROT_READ | PROT_WRITE,
- MAP_SHARED, fd, mmap_start);
-
- if(page == MAP_FAILED){
- fprintf(stderr,"cannot mmap region\n");
- return -1;
- }
-
- while (addr <= endaddr) {
- switch(width){
- case 4: {
- uint32_t* x = (uint32_t*) (((uintptr_t) page) + (addr & 4095));
- if(set) *x = value;
- fprintf(stderr,"%08"PRIxPTR": %08x\n", addr, *x);
- break;
- }
- case 2: {
- uint16_t* x = (uint16_t*) (((uintptr_t) page) + (addr & 4095));
- if(set) *x = value;
- fprintf(stderr,"%08"PRIxPTR": %04x\n", addr, *x);
- break;
- }
- case 1: {
- uint8_t* x = (uint8_t*) (((uintptr_t) page) + (addr & 4095));
- if(set) *x = value;
- fprintf(stderr,"%08"PRIxPTR": %02x\n", addr, *x);
- break;
- }
- }
- addr += width;
- }
- return 0;
-}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/file.c b/toolbox/upstream-netbsd/usr.bin/grep/file.c
index ef057ba..428bf58 100644
--- a/toolbox/upstream-netbsd/usr.bin/grep/file.c
+++ b/toolbox/upstream-netbsd/usr.bin/grep/file.c
@@ -63,7 +63,7 @@
static BZFILE* bzbufdesc;
#endif
-static unsigned char buffer[MAXBUFSIZ];
+static unsigned char buffer[MAXBUFSIZ + 1];
static unsigned char *bufpos;
static size_t bufrem;
@@ -128,7 +128,7 @@
return (0);
}
-static inline int
+static inline void
grep_lnbufgrow(size_t newlen)
{
@@ -136,8 +136,6 @@
lnbuf = grep_realloc(lnbuf, newlen);
lnbuflen = newlen;
}
-
- return (0);
}
char *
@@ -162,20 +160,22 @@
/* Look for a newline in the remaining part of the buffer */
if ((p = memchr(bufpos, line_sep, bufrem)) != NULL) {
++p; /* advance over newline */
- ret = (char *)bufpos;
len = p - bufpos;
+ grep_lnbufgrow(len + 1);
+ memcpy(lnbuf, bufpos, len);
+ lnbuf[len] = '\0';
+ *lenp = len;
bufrem -= len;
bufpos = p;
- *lenp = len;
- return (ret);
+ return ((char *)lnbuf);
}
/* We have to copy the current buffered data to the line buffer */
for (len = bufrem, off = 0; ; len += bufrem) {
/* Make sure there is room for more data */
- if (grep_lnbufgrow(len + LNBUFBUMP))
- goto error;
+ grep_lnbufgrow(len + LNBUFBUMP);
memcpy(lnbuf + off, bufpos, len - off);
+ lnbuf[len] = '\0';
off = len;
if (grep_refill(f) != 0)
goto error;
@@ -188,9 +188,9 @@
++p;
diff = p - bufpos;
len += diff;
- if (grep_lnbufgrow(len))
- goto error;
+ grep_lnbufgrow(len + 1);
memcpy(lnbuf + off, bufpos, diff);
+ lnbuf[off + diff] = '\0';
bufrem -= diff;
bufpos = p;
break;
diff --git a/trusty/trusty-base.mk b/trusty/trusty-base.mk
index 0a0ecec..00e3dbc 100644
--- a/trusty/trusty-base.mk
+++ b/trusty/trusty-base.mk
@@ -19,8 +19,13 @@
# to pull in the baseline set of Trusty specific modules.
#
+# For gatekeeper, we include the generic -service and -impl to use legacy
+# HAL loading of gatekeeper.trusty.
+
PRODUCT_PACKAGES += \
android.hardware.keymaster@3.0-service.trusty \
+ android.hardware.gatekeeper@1.0-service \
+ android.hardware.gatekeeper@1.0-impl \
gatekeeper.trusty
PRODUCT_PROPERTY_OVERRIDES += \