Merge "fs_mgr: enable DT fstab based on its status node appropriately"
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index ae02525..2dcfb53 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -78,7 +78,13 @@
std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
fs_mgr_free_fstab);
struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab.get(), dir);
- return rec ? rec->blk_device : "";
+ if (!rec) {
+ return "";
+ }
+ if (fs_mgr_is_logical(rec)) {
+ fs_mgr_update_logical_partition(rec);
+ }
+ return rec->blk_device;
}
// The proc entry for / is full of lies, so check fstab instead.
@@ -87,7 +93,7 @@
if (is_root) {
return find_fstab_mount(dir);
} else {
- return find_proc_mount(dir);
+ return find_proc_mount(dir);
}
}
@@ -155,11 +161,12 @@
return true;
}
bool is_root = strcmp(dir, "/") == 0;
- if (is_root && !find_mount("/system", false).empty()) {
- dir = "/system";
- is_root = false;
- }
std::string dev = find_mount(dir, is_root);
+ if (is_root && dev.empty()) {
+ // The fstab entry will be /system if the device switched roots during
+ // first-stage init.
+ dev = find_mount("/system", true);
+ }
// Even if the device for the root is not found, we still try to remount it
// as rw. This typically only happens when running Android in a container:
// the root will almost always be in a loop device, which is dynamic, so
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index cd2dc04..4e3879b 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -19,6 +19,7 @@
#include <fcntl.h>
#if !defined(_WIN32)
+#include <dirent.h>
#include <sys/socket.h>
#endif
@@ -211,6 +212,17 @@
return file;
}
+// Using fdopendir with unique_fd correctly is more annoying than it should be,
+// because fdopen doesn't close the file descriptor received upon failure.
+inline DIR* Fdopendir(unique_fd&& ufd) {
+ int fd = ufd.release();
+ DIR* dir = fdopendir(fd);
+ if (dir == nullptr) {
+ close(fd);
+ }
+ return dir;
+}
+
#endif // !defined(_WIN32)
} // namespace base
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 6e45133..11c838a 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -329,7 +329,6 @@
private:
std::string super_device_;
- uint32_t slot_number_;
std::unique_ptr<MetadataBuilder> builder_;
};
@@ -341,8 +340,8 @@
super_device_ = *super_device;
std::string slot = device->GetCurrentSlot();
- slot_number_ = SlotNumberForSlotSuffix(slot);
- builder_ = MetadataBuilder::New(super_device_, slot_number_);
+ uint32_t slot_number = SlotNumberForSlotSuffix(slot);
+ builder_ = MetadataBuilder::New(super_device_, slot_number);
}
bool PartitionBuilder::Write() {
@@ -350,7 +349,11 @@
if (!metadata) {
return false;
}
- return UpdatePartitionTable(super_device_, *metadata.get(), slot_number_);
+ bool ok = true;
+ for (uint32_t i = 0; i < metadata->geometry.metadata_slot_count; i++) {
+ ok &= UpdatePartitionTable(super_device_, *metadata.get(), i);
+ }
+ return ok;
}
bool CreatePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index ae2e2fe..c321fe3 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -1588,6 +1588,25 @@
return true;
}
-std::string fs_mgr_get_super_partition_name(int /* slot */) {
+std::string fs_mgr_get_super_partition_name(int slot) {
+ // Devices upgrading to dynamic partitions are allowed to specify a super
+ // partition name, assumed to be A/B (non-A/B retrofit is not supported).
+ // For devices launching with dynamic partition support, the partition
+ // name must be "super".
+ std::string super_partition;
+ if (fs_mgr_get_boot_config_from_kernel_cmdline("super_partition", &super_partition)) {
+ std::string suffix;
+ if (slot == 0) {
+ suffix = "_a";
+ } else if (slot == 1) {
+ suffix = "_b";
+ } else if (slot == -1) {
+ suffix = fs_mgr_get_slot_suffix();
+ }
+ if (suffix.empty()) {
+ LFATAL << "Super partition name can only be overridden on A/B devices.";
+ }
+ return super_partition + suffix;
+ }
return LP_METADATA_DEFAULT_PARTITION_NAME;
}
diff --git a/fs_mgr/fs_mgr_dm_linear.cpp b/fs_mgr/fs_mgr_dm_linear.cpp
index 4dacebf..fe0e039 100644
--- a/fs_mgr/fs_mgr_dm_linear.cpp
+++ b/fs_mgr/fs_mgr_dm_linear.cpp
@@ -137,13 +137,22 @@
LOG(ERROR) << "Could not read partition table.";
return true;
}
- for (const auto& partition : metadata->partitions) {
+ return CreateLogicalPartitions(*metadata.get());
+}
+
+std::unique_ptr<LpMetadata> ReadCurrentMetadata(const std::string& block_device) {
+ uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());
+ return ReadMetadata(block_device.c_str(), slot);
+}
+
+bool CreateLogicalPartitions(const LpMetadata& metadata) {
+ for (const auto& partition : metadata.partitions) {
if (!partition.num_extents) {
LINFO << "Skipping zero-length logical partition: " << GetPartitionName(partition);
continue;
}
std::string path;
- if (!CreateLogicalPartition(*metadata.get(), partition, false, {}, &path)) {
+ if (!CreateLogicalPartition(metadata, partition, false, {}, &path)) {
LERROR << "Could not create logical partition: " << GetPartitionName(partition);
return false;
}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index f06b819..49ecc06 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -20,6 +20,7 @@
#include <linux/fs.h>
#include <selinux/selinux.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/param.h>
@@ -692,13 +693,21 @@
errno = 0;
}
- auto ret = system((mnt_type == "f2fs")
- ? ((kMkF2fs + " -d1 " + scratch_device).c_str())
- : ((kMkExt4 + " -b 4096 -t ext4 -m 0 -M " + kScratchMountPoint +
- " -O has_journal " + scratch_device)
- .c_str()));
+ // Force mkfs by design for overlay support of adb remount, simplify and
+ // thus do not rely on fsck to correct problems that could creep in.
+ auto command = ""s;
+ if (mnt_type == "f2fs") {
+ command = kMkF2fs + " -w 4096 -f -d1 -l" + android::base::Basename(kScratchMountPoint);
+ } else if (mnt_type == "ext4") {
+ command = kMkExt4 + " -b 4096 -t ext4 -m 0 -O has_journal -M " + kScratchMountPoint;
+ } else {
+ LERROR << mnt_type << " has no mkfs cookbook";
+ return false;
+ }
+ command += " " + scratch_device;
+ auto ret = system(command.c_str());
if (ret) {
- LERROR << "make " << mnt_type << " filesystem on " << scratch_device << " error=" << ret;
+ LERROR << "make " << mnt_type << " filesystem on " << scratch_device << " return=" << ret;
return false;
}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 5e83cfb..23a92d3 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -39,11 +39,13 @@
#define LINFO LOG(INFO) << FS_MGR_TAG
#define LWARNING LOG(WARNING) << FS_MGR_TAG
#define LERROR LOG(ERROR) << FS_MGR_TAG
+#define LFATAL LOG(FATAL) << FS_MGR_TAG
// Logs a message with strerror(errno) at the end
#define PINFO PLOG(INFO) << FS_MGR_TAG
#define PWARNING PLOG(WARNING) << FS_MGR_TAG
#define PERROR PLOG(ERROR) << FS_MGR_TAG
+#define PFATAL PLOG(FATAL) << FS_MGR_TAG
#define CRYPTO_TMPFS_OPTIONS "size=512m,mode=0771,uid=1000,gid=1000"
diff --git a/fs_mgr/fs_mgr_vendor_overlay.cpp b/fs_mgr/fs_mgr_vendor_overlay.cpp
index 360a117..a9a69cd 100644
--- a/fs_mgr/fs_mgr_vendor_overlay.cpp
+++ b/fs_mgr/fs_mgr_vendor_overlay.cpp
@@ -74,16 +74,21 @@
const auto vendor_mount_point = kVendorTopDir + mount_point;
LINFO << "vendor overlay mount on " << vendor_mount_point;
- auto context = fs_mgr_get_context(vendor_mount_point);
- if (!context.empty()) {
- context = ",rootcontext="s + context;
- } else {
- PERROR << " result: cannot find the mount point";
+ const auto target_context = fs_mgr_get_context(vendor_mount_point);
+ if (target_context.empty()) {
+ PERROR << " failed: cannot find the target vendor mount point";
+ return false;
+ }
+ const auto source_directory = overlay_top + "/" + mount_point;
+ const auto source_context = fs_mgr_get_context(source_directory);
+ if (target_context != source_context) {
+ LERROR << " failed: source and target contexts do not match (source:" << source_context
+ << ", target:" << target_context << ")";
return false;
}
- auto options = "override_creds=off,"s + kLowerdirOption + overlay_top + "/" + mount_point +
- ":" + vendor_mount_point + context;
+ auto options =
+ "override_creds=off,"s + kLowerdirOption + source_directory + ":" + vendor_mount_point;
auto report = "__mount(source=overlay,target="s + vendor_mount_point + ",type=overlay," +
options + ")=";
auto ret = mount("overlay", vendor_mount_point.c_str(), "overlay", MS_RDONLY | MS_RELATIME,
diff --git a/fs_mgr/include/fs_mgr_dm_linear.h b/fs_mgr/include/fs_mgr_dm_linear.h
index 08f4554..66abfca 100644
--- a/fs_mgr/include/fs_mgr_dm_linear.h
+++ b/fs_mgr/include/fs_mgr_dm_linear.h
@@ -33,11 +33,20 @@
#include <vector>
#include <libdm/dm.h>
-#include <liblp/metadata_format.h>
+#include <liblp/liblp.h>
namespace android {
namespace fs_mgr {
+// Read metadata from the current slot.
+std::unique_ptr<LpMetadata> ReadCurrentMetadata(const std::string& block_device);
+
+// Create block devices for all logical partitions in the given metadata. The
+// metadata must have been read from the current slot.
+bool CreateLogicalPartitions(const LpMetadata& metadata);
+
+// Create block devices for all logical partitions. This is a convenience
+// method for ReadMetadata and CreateLogicalPartitions.
bool CreateLogicalPartitions(const std::string& block_device);
// Create a block device for a single logical partition, given metadata and
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index 4669cea..a5dac00 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -90,6 +90,9 @@
// Return the total size of all partitions comprising the super partition.
uint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata);
+// Get the list of block device names required by the given metadata.
+std::vector<std::string> GetBlockDevicePartitionNames(const LpMetadata& metadata);
+
// Helper to return a slot number for a slot suffix.
uint32_t SlotNumberForSlotSuffix(const std::string& suffix);
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 742ad82..199d994 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -116,5 +116,13 @@
return size;
}
+std::vector<std::string> GetBlockDevicePartitionNames(const LpMetadata& metadata) {
+ std::vector<std::string> list;
+ for (const auto& block_device : metadata.block_devices) {
+ list.emplace_back(GetBlockDevicePartitionName(block_device));
+ }
+ return list;
+}
+
} // namespace fs_mgr
} // namespace android
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index c740bd4..f4c9b99 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -82,12 +82,8 @@
// Perform sanity checks so we don't accidentally overwrite valid metadata
// with potentially invalid metadata, or random partition data with metadata.
-static bool ValidateAndSerializeMetadata(int fd, const LpMetadata& metadata, std::string* blob) {
- uint64_t blockdevice_size;
- if (!GetDescriptorSize(fd, &blockdevice_size)) {
- return false;
- }
-
+static bool ValidateAndSerializeMetadata(const IPartitionOpener& opener, const LpMetadata& metadata,
+ std::string* blob) {
const LpMetadataHeader& header = metadata.header;
const LpMetadataGeometry& geometry = metadata.geometry;
@@ -104,7 +100,7 @@
// metadata.
uint64_t reserved_size = LP_METADATA_GEOMETRY_SIZE +
uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;
- uint64_t total_reserved = reserved_size * 2;
+ uint64_t total_reserved = LP_PARTITION_RESERVED_BYTES + reserved_size * 2;
const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);
if (!super_device) {
@@ -112,15 +108,27 @@
return false;
}
- if (total_reserved > blockdevice_size ||
- total_reserved > super_device->first_logical_sector * LP_SECTOR_SIZE) {
+ if (total_reserved > super_device->first_logical_sector * LP_SECTOR_SIZE) {
LERROR << "Not enough space to store all logical partition metadata slots.";
return false;
}
- if (blockdevice_size != super_device->size) {
- LERROR << "Block device size " << blockdevice_size
- << " does not match metadata requested size " << super_device->size;
- return false;
+ for (const auto& block_device : metadata.block_devices) {
+ std::string partition_name = GetBlockDevicePartitionName(block_device);
+ if ((block_device.first_logical_sector + 1) * LP_SECTOR_SIZE > block_device.size) {
+ LERROR << "Block device " << partition_name << " has invalid first sector "
+ << block_device.first_logical_sector << " for size " << block_device.size;
+ return false;
+ }
+ BlockDeviceInfo info;
+ if (!opener.GetInfo(partition_name, &info)) {
+ PERROR << partition_name << ": ioctl";
+ return false;
+ }
+ if (info.size != block_device.size) {
+ LERROR << "Block device " << partition_name << " size mismatch (expected"
+ << block_device.size << ", got " << info.size << ")";
+ return false;
+ }
}
// Make sure all partition entries reference valid extents.
@@ -230,7 +238,7 @@
// basic checks that the geometry and tables are coherent, and will fit
// on the given block device.
std::string metadata_blob;
- if (!ValidateAndSerializeMetadata(fd, metadata, &metadata_blob)) {
+ if (!ValidateAndSerializeMetadata(opener, metadata, &metadata_blob)) {
return false;
}
@@ -295,7 +303,7 @@
// basic checks that the geometry and tables are coherent, and will fit
// on the given block device.
std::string blob;
- if (!ValidateAndSerializeMetadata(fd, metadata, &blob)) {
+ if (!ValidateAndSerializeMetadata(opener, metadata, &blob)) {
return false;
}
@@ -327,7 +335,7 @@
// synchronize the backup copy. This guarantees that a partial write
// still leaves one copy intact.
std::string old_blob;
- if (!ValidateAndSerializeMetadata(fd, *primary.get(), &old_blob)) {
+ if (!ValidateAndSerializeMetadata(opener, *primary.get(), &old_blob)) {
LERROR << "Error serializing primary metadata to repair corrupted backup";
return false;
}
@@ -339,7 +347,7 @@
// The backup copy is coherent, and the primary is not. Sync it for
// safety.
std::string old_blob;
- if (!ValidateAndSerializeMetadata(fd, *backup.get(), &old_blob)) {
+ if (!ValidateAndSerializeMetadata(opener, *backup.get(), &old_blob)) {
LERROR << "Error serializing primary metadata to repair corrupted backup";
return false;
}
diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh
index b6a8eef..6b908d3 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -198,39 +198,80 @@
# Do something
adb_wait || die "wait for device failed"
-adb_sh ls -d /sys/module/overlay </dev/null || die "overlay module not present"
-adb_su ls /sys/module/overlay/parameters/override_creds </dev/null ||
+adb_sh ls -d /sys/module/overlay </dev/null >/dev/null &&
+ echo "${GREEN}[ OK ]${NORMAL} overlay module present" >&2 ||
+ die "overlay module not present"
+adb_su ls /sys/module/overlay/parameters/override_creds </dev/null >/dev/null &&
+ echo "${GREEN}[ OK ]${NORMAL} overlay module supports override_creds" >&2 ||
die "overlay module can not be used on ANDROID"
adb_root &&
- adb_wait &&
- D=`adb disable-verity 2>&1` ||
- die "setup for overlay"
+ adb_wait ||
+ die "initial setup"
+reboot=false
+OVERLAYFS_BACKING="cache mnt/scratch"
+for d in ${OVERLAYFS_BACKING}; do
+ if adb_sh ls -d /${d}/overlay </dev/null >/dev/null 2>&1; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} /${d}/overlay is setup, wiping" >&2
+ adb_sh rm -rf /${d}/overlay </dev/null ||
+ die "/${d}/overlay wipe"
+ reboot=true
+ fi
+done
+if ${reboot}; then
+ echo "${ORANGE}[ WARNING ]${NORMAL} rebooting before test" >&2
+ adb_reboot &&
+ adb_wait 2m &&
+ adb_root &&
+ adb_wait ||
+ die "reboot after wipe"
+fi
+D=`adb_sh df -k </dev/null` &&
+ H=`echo "${D}" | head -1` &&
+ D=`echo "${D}" | grep "^overlay "` &&
+ echo "${H}" &&
+ echo "${D}" &&
+ echo "${ORANGE}[ WARNING ]${NORMAL} overlays present before setup" >&2 ||
+ echo "${GREEN}[ OK ]${NORMAL} no overlay present before setup" >&2
+
+D=`adb disable-verity 2>&1` ||
+ die "setup for overlay ${D}"
echo "${D}"
if [ X"${D}" != X"${D##*using overlayfs}" ]; then
echo "${GREEN}[ OK ]${NORMAL} using overlayfs" >&2
fi
-if adb_sh ls -d /data/overlay </dev/null >/dev/null 2>&1; then
- echo "/data/overlay setup, clearing out" >&2
- adb_sh rm -rf /data/overlay </dev/null ||
- die "/data/overlay removal"
-fi
-adb_sh ls -d /cache/overlay </dev/null >/dev/null 2>&1 ||
- adb_sh ls -d /mnt/scratch/overlay </dev/null >/dev/null 2>&1 ||
- die "overlay directory setup"
adb_reboot &&
adb_wait &&
- adb_sh df -k </dev/null | head -1 &&
- adb_sh df -k </dev/null | grep "^overlay " ||
+ D=`adb_sh df -k </dev/null` &&
+ H=`echo "${D}" | head -1` &&
+ D=`echo "${D}" | grep "^overlay "` &&
+ echo "${H}" &&
+ echo "${D}" ||
die "overlay takeover failed"
-adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
+echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
echo "${ORANGE}[ WARNING ]${NORMAL} overlay takeover before remount not complete" >&2
adb_root &&
adb_wait &&
adb remount &&
- adb_sh df -k </dev/null | head -1 &&
- adb_sh df -k </dev/null | grep "^overlay " &&
- adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
+ D=`adb_sh df -k </dev/null` ||
+ die "can not collect filesystem data"
+if echo "${D}" | grep " /mnt/scratch" >/dev/null; then
+ echo "${ORANGE}[ INFO ]${NORMAL} using scratch dynamic partition for overrides" >&2
+ H=`adb_sh cat /proc/mounts | sed -n 's@\([^ ]*\) /mnt/scratch \([^ ]*\) .*@\2 on \1@p'`
+ [ -n "${H}" ] &&
+ echo "${ORANGE}[ INFO ]${NORMAL} scratch filesystem ${H}"
+fi
+for d in ${OVERLAYFS_BACKING}; do
+ if adb_sh ls -d /${d}/overlay/system/upper </dev/null >/dev/null 2>&1; then
+ echo "${ORANGE}[ INFO ]${NORMAL} /${d}/overlay is setup" >&2
+ fi
+done
+
+H=`echo "${D}" | head -1` &&
+ D=`echo "${D}" | grep "^overlay "` &&
+ echo "${H}" &&
+ echo "${D}" &&
+ echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
die "overlay takeover after remount"
!(adb_sh grep "^overlay " /proc/mounts </dev/null | grep " overlay ro,") &&
!(adb_sh grep " rw," /proc/mounts </dev/null |
@@ -275,11 +316,14 @@
adb_wait &&
adb_root &&
adb_wait &&
- adb_sh df -k </dev/null | head -1 &&
- adb_sh df -k </dev/null | grep "^overlay " &&
- adb_sh df -k </dev/null | grep "^overlay .* /system\$" >/dev/null ||
+ D=`adb_sh df -k </dev/null` &&
+ H=`echo "${D}" | head -1` &&
+ D=`echo "${D}" | grep "^overlay "` &&
+ echo "${H}" &&
+ echo "${D}" &&
+ echo "${D}" | grep "^overlay .* /system\$" >/dev/null ||
die "overlay system takeover after flash vendor"
-adb_sh df -k </dev/null | grep "^overlay .* /vendor\$" >/dev/null &&
+echo "${D}" | grep "^overlay .* /vendor\$" >/dev/null &&
die "overlay minus vendor takeover after flash vendor"
B="`adb_cat /system/hello`" ||
die "re-read system hello after flash vendor"
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 2a5667c..a1519da 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -240,10 +240,9 @@
if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)
props.batteryTechnology = String8(buf.c_str());
- unsigned int i;
double MaxPower = 0;
- for (i = 0; i < mChargerNames.size(); i++) {
+ for (size_t i = 0; i < mChargerNames.size(); i++) {
String8 path;
path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,
mChargerNames[i].string());
diff --git a/init/Android.mk b/init/Android.mk
index c85727c..dc46d21 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -90,6 +90,8 @@
libcap \
LOCAL_SANITIZE := signed-integer-overflow
+# First stage init is weird: it may start without stdout/stderr, and no /proc.
+LOCAL_NOSANITIZE := hwaddress
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
diff --git a/init/devices.cpp b/init/devices.cpp
index 58c8b2e..45b17a2 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -419,7 +419,7 @@
}
void DeviceHandler::ColdbootDone() {
- skip_restorecon_ = true;
+ skip_restorecon_ = false;
}
DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index eb86eb0..13a9d08 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -33,7 +33,7 @@
#include <fs_mgr_avb.h>
#include <fs_mgr_dm_linear.h>
#include <fs_mgr_overlayfs.h>
-#include <liblp/metadata_format.h>
+#include <liblp/liblp.h>
#include "devices.h"
#include "switch_root.h"
@@ -69,7 +69,8 @@
bool MountPartition(fstab_rec* fstab_rec);
bool MountPartitions();
bool IsDmLinearEnabled();
- bool GetBackingDmLinearDevices();
+ bool GetDmLinearMetadataDevice();
+ bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
virtual ListenerAction UeventCallback(const Uevent& uevent);
@@ -183,7 +184,7 @@
}
bool FirstStageMount::InitDevices() {
- return GetBackingDmLinearDevices() && GetDmVerityDevices() && InitRequiredDevices();
+ return GetDmLinearMetadataDevice() && GetDmVerityDevices() && InitRequiredDevices();
}
bool FirstStageMount::IsDmLinearEnabled() {
@@ -193,7 +194,7 @@
return false;
}
-bool FirstStageMount::GetBackingDmLinearDevices() {
+bool FirstStageMount::GetDmLinearMetadataDevice() {
// Add any additional devices required for dm-linear mappings.
if (!IsDmLinearEnabled()) {
return true;
@@ -258,17 +259,48 @@
return true;
}
+bool FirstStageMount::InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata) {
+ auto partition_names = GetBlockDevicePartitionNames(metadata);
+ for (const auto& partition_name : partition_names) {
+ if (partition_name == lp_metadata_partition_) {
+ continue;
+ }
+ required_devices_partition_names_.emplace(partition_name);
+ }
+ if (required_devices_partition_names_.empty()) {
+ return true;
+ }
+
+ auto uevent_callback = [this](const Uevent& uevent) { return UeventCallback(uevent); };
+ uevent_listener_.RegenerateUevents(uevent_callback);
+
+ if (!required_devices_partition_names_.empty()) {
+ LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
+ << android::base::Join(required_devices_partition_names_, ", ");
+ return false;
+ }
+ return true;
+}
+
bool FirstStageMount::CreateLogicalPartitions() {
if (!IsDmLinearEnabled()) {
return true;
}
-
if (lp_metadata_partition_.empty()) {
LOG(ERROR) << "Could not locate logical partition tables in partition "
<< super_partition_name_;
return false;
}
- return android::fs_mgr::CreateLogicalPartitions(lp_metadata_partition_);
+
+ auto metadata = android::fs_mgr::ReadCurrentMetadata(lp_metadata_partition_);
+ if (!metadata) {
+ LOG(ERROR) << "Could not read logical partition metadata from " << lp_metadata_partition_;
+ return false;
+ }
+ if (!InitDmLinearBackingDevices(*metadata.get())) {
+ return false;
+ }
+ return android::fs_mgr::CreateLogicalPartitions(*metadata.get());
}
ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent) {
diff --git a/liblog/include/android/log.h b/liblog/include/android/log.h
index ee9220d..b2f0ed9 100644
--- a/liblog/include/android/log.h
+++ b/liblog/include/android/log.h
@@ -14,25 +14,7 @@
* limitations under the License.
*/
-#ifndef _ANDROID_LOG_H
-#define _ANDROID_LOG_H
-
-/******************************************************************
- *
- * IMPORTANT NOTICE:
- *
- * This file is part of Android's set of stable system headers
- * exposed by the Android NDK (Native Development Kit) since
- * platform release 1.5
- *
- * Third-party source AND binary code relies on the definitions
- * here to be FROZEN ON ALL UPCOMING PLATFORM RELEASES.
- *
- * - DO NOT MODIFY ENUMS (EXCEPT IF YOU ADD NEW 32-BIT VALUES)
- * - DO NOT MODIFY CONSTANTS OR FUNCTIONAL MACROS
- * - DO NOT CHANGE THE SIGNATURE OF FUNCTIONS IN ANY WAY
- * - DO NOT CHANGE THE LAYOUT OR SIZE OF STRUCTURES
- */
+#pragma once
/**
* @addtogroup Logging
@@ -154,27 +136,51 @@
#ifndef log_id_t_defined
#define log_id_t_defined
+/**
+ * Identifies a specific log buffer for __android_log_buf_write()
+ * and __android_log_buf_print().
+ */
typedef enum log_id {
LOG_ID_MIN = 0,
+ /** The main log buffer. This is the only log buffer available to apps. */
LOG_ID_MAIN = 0,
+ /** The radio log buffer. */
LOG_ID_RADIO = 1,
+ /** The event log buffer. */
LOG_ID_EVENTS = 2,
+ /** The system log buffer. */
LOG_ID_SYSTEM = 3,
+ /** The crash log buffer. */
LOG_ID_CRASH = 4,
+ /** The statistics log buffer. */
LOG_ID_STATS = 5,
+ /** The security log buffer. */
LOG_ID_SECURITY = 6,
- LOG_ID_KERNEL = 7, /* place last, third-parties can not use it */
+ /** The kernel log buffer. */
+ LOG_ID_KERNEL = 7,
LOG_ID_MAX
} log_id_t;
#endif
-/*
- * Send a simple string to the log.
+/**
+ * Writes the constant string `text` to the log buffer `id`,
+ * with priority `prio` and tag `tag`.
+ *
+ * Apps should use __android_log_write() instead.
*/
int __android_log_buf_write(int bufID, int prio, const char* tag,
const char* text);
+
+/**
+ * Writes a formatted string to log buffer `id`,
+ * with priority `prio` and tag `tag`.
+ * The details of formatting are the same as for
+ * [printf(3)](http://man7.org/linux/man-pages/man3/printf.3.html).
+ *
+ * Apps should use __android_log_print() instead.
+ */
int __android_log_buf_print(int bufID, int prio, const char* tag,
const char* fmt, ...)
#if defined(__GNUC__)
@@ -187,5 +193,3 @@
#endif
/** @} */
-
-#endif /* _ANDROID_LOG_H */
diff --git a/libmeminfo/.clang-format b/libmeminfo/.clang-format
new file mode 120000
index 0000000..1af4f51
--- /dev/null
+++ b/libmeminfo/.clang-format
@@ -0,0 +1 @@
+../.clang-format-4
\ No newline at end of file
diff --git a/libmeminfo/Android.bp b/libmeminfo/Android.bp
new file mode 100644
index 0000000..aab3743
--- /dev/null
+++ b/libmeminfo/Android.bp
@@ -0,0 +1,70 @@
+//
+// 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.
+//
+
+cc_defaults {
+ name: "libmeminfo_defaults",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libprocinfo",
+ ],
+}
+
+cc_library {
+ name: "libmeminfo",
+ defaults: ["libmeminfo_defaults"],
+ export_include_dirs: ["include"],
+ export_shared_lib_headers: ["libbase"],
+ srcs: [
+ "pageacct.cpp",
+ "procmeminfo.cpp",
+ "sysmeminfo.cpp",
+ ],
+}
+
+cc_test {
+ name: "libmeminfo_test",
+ defaults: ["libmeminfo_defaults"],
+
+ static_libs: [
+ "libmeminfo",
+ "libpagemap",
+ "libbase",
+ "liblog",
+ ],
+
+ srcs: [
+ "libmeminfo_test.cpp"
+ ],
+}
+
+cc_benchmark {
+ name: "libmeminfo_benchmark",
+ srcs: [
+ "libmeminfo_benchmark.cpp",
+ ],
+ static_libs : [
+ "libbase",
+ "liblog",
+ "libmeminfo",
+ "libprocinfo",
+ ],
+}
diff --git a/libmeminfo/include/meminfo/meminfo.h b/libmeminfo/include/meminfo/meminfo.h
new file mode 100644
index 0000000..c328648
--- /dev/null
+++ b/libmeminfo/include/meminfo/meminfo.h
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+namespace android {
+namespace meminfo {
+
+struct MemUsage {
+ uint64_t vss;
+ uint64_t rss;
+ uint64_t pss;
+ uint64_t uss;
+
+ uint64_t private_clean;
+ uint64_t private_dirty;
+ uint64_t shared_clean;
+ uint64_t shared_dirty;
+
+ MemUsage()
+ : vss(0),
+ rss(0),
+ pss(0),
+ uss(0),
+ private_clean(0),
+ private_dirty(0),
+ shared_clean(0),
+ shared_dirty(0) {}
+
+ ~MemUsage() = default;
+
+ void clear() {
+ vss = rss = pss = uss = 0;
+ private_clean = private_dirty = shared_clean = shared_dirty = 0;
+ }
+};
+
+struct Vma {
+ uint64_t start;
+ uint64_t end;
+ uint64_t offset;
+ uint16_t flags;
+ std::string name;
+
+ Vma(uint64_t s, uint64_t e, uint64_t off, uint16_t f, const char* n)
+ : start(s), end(e), offset(off), flags(f), name(n) {}
+ ~Vma() = default;
+
+ // Memory usage of this mapping.
+ MemUsage usage;
+ // Working set within this mapping.
+ MemUsage wss;
+};
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/include/meminfo/pageacct.h b/libmeminfo/include/meminfo/pageacct.h
new file mode 100644
index 0000000..8ddaef2
--- /dev/null
+++ b/libmeminfo/include/meminfo/pageacct.h
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace meminfo {
+
+class PageAcct final {
+ // Class for per-page accounting by using kernel provided interfaces like
+ // kpagecount, kpageflags etc.
+ public:
+ static bool KernelHasPageIdle() {
+ return (access("/sys/kernel/mm/page_idle/bitmap", R_OK | W_OK) == 0);
+ }
+
+ bool InitPageAcct(bool pageidle_enable = false);
+ bool PageFlags(uint64_t pfn, uint64_t* flags);
+ bool PageMapCount(uint64_t pfn, uint64_t* mapcount);
+
+ int IsPageIdle(uint64_t pfn);
+
+ // The only way to create PageAcct object
+ static PageAcct& Instance() {
+ static PageAcct instance;
+ return instance;
+ }
+
+ ~PageAcct() = default;
+
+ private:
+ PageAcct() : kpagecount_fd_(-1), kpageflags_fd_(-1), pageidle_fd_(-1) {}
+ int MarkPageIdle(uint64_t pfn) const;
+ int GetPageIdle(uint64_t pfn) const;
+
+ // Non-copyable & Non-movable
+ PageAcct(const PageAcct&) = delete;
+ PageAcct& operator=(const PageAcct&) = delete;
+ PageAcct& operator=(PageAcct&&) = delete;
+ PageAcct(PageAcct&&) = delete;
+
+ ::android::base::unique_fd kpagecount_fd_;
+ ::android::base::unique_fd kpageflags_fd_;
+ ::android::base::unique_fd pageidle_fd_;
+};
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/include/meminfo/procmeminfo.h b/libmeminfo/include/meminfo/procmeminfo.h
new file mode 100644
index 0000000..b37c56b
--- /dev/null
+++ b/libmeminfo/include/meminfo/procmeminfo.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include "meminfo.h"
+
+namespace android {
+namespace meminfo {
+
+class ProcMemInfo final {
+ // Per-process memory accounting
+ public:
+ ProcMemInfo(pid_t pid, bool get_wss = false);
+
+ const std::vector<Vma>& Maps();
+ const MemUsage& Usage();
+ const MemUsage& Wss();
+
+ bool WssReset();
+ ~ProcMemInfo() = default;
+
+ private:
+ bool ReadMaps(bool get_wss);
+ bool ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss);
+
+ pid_t pid_;
+ bool get_wss_;
+
+ std::vector<Vma> maps_;
+
+ MemUsage usage_;
+ MemUsage wss_;
+};
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/include/meminfo/sysmeminfo.h b/libmeminfo/include/meminfo/sysmeminfo.h
new file mode 100644
index 0000000..f5e05bd
--- /dev/null
+++ b/libmeminfo/include/meminfo/sysmeminfo.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace meminfo {
+
+class SysMemInfo final {
+ // System or Global memory accounting
+ public:
+ static const std::vector<std::string> kDefaultSysMemInfoTags;
+
+ SysMemInfo() = default;
+
+ // Parse /proc/meminfo and read values that are needed
+ bool ReadMemInfo(const std::string& path = "/proc/meminfo");
+ bool ReadMemInfo(const std::vector<std::string>& tags,
+ const std::string& path = "/proc/meminfo");
+
+ // getters
+ uint64_t mem_total_kb() { return mem_in_kb_["MemTotal:"]; }
+ uint64_t mem_free_kb() { return mem_in_kb_["MemFree:"]; }
+ uint64_t mem_buffers_kb() { return mem_in_kb_["Buffers:"]; }
+ uint64_t mem_cached_kb() { return mem_in_kb_["Cached:"]; }
+ uint64_t mem_shmem_kb() { return mem_in_kb_["Shmem:"]; }
+ uint64_t mem_slab_kb() { return mem_in_kb_["Slab:"]; }
+ uint64_t mem_slab_reclailmable_kb() { return mem_in_kb_["SReclaimable:"]; }
+ uint64_t mem_slab_unreclaimable_kb() { return mem_in_kb_["SUnreclaim:"]; }
+ uint64_t mem_swap_kb() { return mem_in_kb_["SwapTotal:"]; }
+ uint64_t mem_free_swap_kb() { return mem_in_kb_["SwapFree:"]; }
+ uint64_t mem_zram_kb() { return mem_in_kb_["Zram:"]; }
+ uint64_t mem_mapped_kb() { return mem_in_kb_["Mapped:"]; }
+ uint64_t mem_vmalloc_used_kb() { return mem_in_kb_["VmallocUsed:"]; }
+ uint64_t mem_page_tables_kb() { return mem_in_kb_["PageTables:"]; }
+ uint64_t mem_kernel_stack_kb() { return mem_in_kb_["KernelStack:"]; }
+
+ private:
+ std::map<std::string, uint64_t> mem_in_kb_;
+};
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/libmeminfo_benchmark.cpp b/libmeminfo/libmeminfo_benchmark.cpp
new file mode 100644
index 0000000..3820776b
--- /dev/null
+++ b/libmeminfo/libmeminfo_benchmark.cpp
@@ -0,0 +1,218 @@
+/*
+ * 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 <meminfo/sysmeminfo.h>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/test_utils.h>
+
+#include <benchmark/benchmark.h>
+
+enum {
+ MEMINFO_TOTAL,
+ MEMINFO_FREE,
+ MEMINFO_BUFFERS,
+ MEMINFO_CACHED,
+ MEMINFO_SHMEM,
+ MEMINFO_SLAB,
+ MEMINFO_SLAB_RECLAIMABLE,
+ MEMINFO_SLAB_UNRECLAIMABLE,
+ MEMINFO_SWAP_TOTAL,
+ MEMINFO_SWAP_FREE,
+ MEMINFO_ZRAM_TOTAL,
+ MEMINFO_MAPPED,
+ MEMINFO_VMALLOC_USED,
+ MEMINFO_PAGE_TABLES,
+ MEMINFO_KERNEL_STACK,
+ MEMINFO_COUNT
+};
+
+void get_mem_info(uint64_t mem[], const char* file) {
+ char buffer[4096];
+ unsigned int numFound = 0;
+
+ int fd = open(file, O_RDONLY);
+
+ if (fd < 0) {
+ printf("Unable to open %s: %s\n", file, strerror(errno));
+ return;
+ }
+
+ const int len = read(fd, buffer, sizeof(buffer) - 1);
+ close(fd);
+
+ if (len < 0) {
+ printf("Empty %s\n", file);
+ return;
+ }
+ buffer[len] = 0;
+
+ static const char* const tags[] = {
+ "MemTotal:", "MemFree:", "Buffers:", "Cached:", "Shmem:", "Slab:",
+ "SReclaimable:", "SUnreclaim:", "SwapTotal:", "SwapFree:", "ZRam:", "Mapped:",
+ "VmallocUsed:", "PageTables:", "KernelStack:", NULL};
+
+ static const int tagsLen[] = {9, 8, 8, 7, 6, 5, 13, 11, 10, 9, 5, 7, 12, 11, 12, 0};
+
+ memset(mem, 0, sizeof(uint64_t) * 15);
+ char* p = buffer;
+ while (*p && (numFound < (sizeof(tagsLen) / sizeof(tagsLen[0])))) {
+ int i = 0;
+ while (tags[i]) {
+ //std::cout << "tag =" << tags[i] << " p = " << std::string(p, tagsLen[i]) << std::endl;
+ if (strncmp(p, tags[i], tagsLen[i]) == 0) {
+ p += tagsLen[i];
+ while (*p == ' ') p++;
+ char* num = p;
+ while (*p >= '0' && *p <= '9') p++;
+ if (*p != 0) {
+ *p = 0;
+ p++;
+ }
+ mem[i] = atoll(num);
+ numFound++;
+ break;
+ }
+ i++;
+ }
+ while (*p && *p != '\n') {
+ p++;
+ }
+ if (*p) p++;
+ }
+}
+
+static void BM_ParseSysMemInfo(benchmark::State& state) {
+ std::string meminfo = R"meminfo(MemTotal: 3019740 kB
+MemFree: 1809728 kB
+MemAvailable: 2546560 kB
+Buffers: 54736 kB
+Cached: 776052 kB
+SwapCached: 0 kB
+Active: 445856 kB
+Inactive: 459092 kB
+Active(anon): 78492 kB
+Inactive(anon): 2240 kB
+Active(file): 367364 kB
+Inactive(file): 456852 kB
+Unevictable: 3096 kB
+Mlocked: 3096 kB
+SwapTotal: 0 kB
+SwapFree: 0 kB
+Dirty: 32 kB
+Writeback: 0 kB
+AnonPages: 74988 kB
+Mapped: 62624 kB
+Shmem: 4020 kB
+Slab: 86464 kB
+SReclaimable: 44432 kB
+SUnreclaim: 42032 kB
+KernelStack: 4880 kB
+PageTables: 2900 kB
+NFS_Unstable: 0 kB
+Bounce: 0 kB
+WritebackTmp: 0 kB
+CommitLimit: 1509868 kB
+Committed_AS: 80296 kB
+VmallocTotal: 263061440 kB
+VmallocUsed: 0 kB
+VmallocChunk: 0 kB
+AnonHugePages: 6144 kB
+ShmemHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+CmaTotal: 131072 kB
+CmaFree: 130380 kB
+HugePages_Total: 0
+HugePages_Free: 0
+HugePages_Rsvd: 0
+HugePages_Surp: 0
+Hugepagesize: 2048 kB)meminfo";
+
+ TemporaryFile tf;
+ ::android::base::WriteStringToFd(meminfo, tf.fd);
+
+ uint64_t mem[MEMINFO_COUNT];
+ for (auto _ : state) {
+ get_mem_info(mem, tf.path);
+ }
+}
+BENCHMARK(BM_ParseSysMemInfo);
+
+static void BM_ReadMemInfo(benchmark::State& state) {
+ std::string meminfo = R"meminfo(MemTotal: 3019740 kB
+MemFree: 1809728 kB
+MemAvailable: 2546560 kB
+Buffers: 54736 kB
+Cached: 776052 kB
+SwapCached: 0 kB
+Active: 445856 kB
+Inactive: 459092 kB
+Active(anon): 78492 kB
+Inactive(anon): 2240 kB
+Active(file): 367364 kB
+Inactive(file): 456852 kB
+Unevictable: 3096 kB
+Mlocked: 3096 kB
+SwapTotal: 0 kB
+SwapFree: 0 kB
+Dirty: 32 kB
+Writeback: 0 kB
+AnonPages: 74988 kB
+Mapped: 62624 kB
+Shmem: 4020 kB
+Slab: 86464 kB
+SReclaimable: 44432 kB
+SUnreclaim: 42032 kB
+KernelStack: 4880 kB
+PageTables: 2900 kB
+NFS_Unstable: 0 kB
+Bounce: 0 kB
+WritebackTmp: 0 kB
+CommitLimit: 1509868 kB
+Committed_AS: 80296 kB
+VmallocTotal: 263061440 kB
+VmallocUsed: 0 kB
+VmallocChunk: 0 kB
+AnonHugePages: 6144 kB
+ShmemHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+CmaTotal: 131072 kB
+CmaFree: 130380 kB
+HugePages_Total: 0
+HugePages_Free: 0
+HugePages_Rsvd: 0
+HugePages_Surp: 0
+Hugepagesize: 2048 kB)meminfo";
+
+ TemporaryFile tf;
+ android::base::WriteStringToFd(meminfo, tf.fd);
+
+ std::string file = std::string(tf.path);
+ ::android::meminfo::SysMemInfo mi;
+ for (auto _ : state) {
+ mi.ReadMemInfo(file);
+ }
+}
+BENCHMARK(BM_ReadMemInfo);
+
+BENCHMARK_MAIN();
diff --git a/libmeminfo/libmeminfo_test.cpp b/libmeminfo/libmeminfo_test.cpp
new file mode 100644
index 0000000..22f3585
--- /dev/null
+++ b/libmeminfo/libmeminfo_test.cpp
@@ -0,0 +1,301 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+#include <meminfo/pageacct.h>
+#include <meminfo/procmeminfo.h>
+#include <meminfo/sysmeminfo.h>
+#include <pagemap/pagemap.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/test_utils.h>
+
+using namespace std;
+using namespace android::meminfo;
+
+pid_t pid = -1;
+
+class ValidateProcMemInfo : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ASSERT_EQ(0, pm_kernel_create(&ker));
+ ASSERT_EQ(0, pm_process_create(ker, pid, &proc));
+ proc_mem = new ProcMemInfo(pid);
+ ASSERT_NE(proc_mem, nullptr);
+ }
+
+ void TearDown() override {
+ delete proc_mem;
+ pm_process_destroy(proc);
+ pm_kernel_destroy(ker);
+ }
+
+ pm_kernel_t* ker;
+ pm_process_t* proc;
+ ProcMemInfo* proc_mem;
+};
+
+TEST_F(ValidateProcMemInfo, TestMapsSize) {
+ const std::vector<Vma>& maps = proc_mem->Maps();
+ ASSERT_FALSE(maps.empty()) << "Process " << getpid() << " maps are empty";
+}
+
+TEST_F(ValidateProcMemInfo, TestMapsEquality) {
+ const std::vector<Vma>& maps = proc_mem->Maps();
+ ASSERT_EQ(proc->num_maps, maps.size());
+
+ for (size_t i = 0; i < maps.size(); ++i) {
+ EXPECT_EQ(proc->maps[i]->start, maps[i].start);
+ EXPECT_EQ(proc->maps[i]->end, maps[i].end);
+ EXPECT_EQ(proc->maps[i]->offset, maps[i].offset);
+ EXPECT_EQ(std::string(proc->maps[i]->name), maps[i].name);
+ }
+}
+
+TEST_F(ValidateProcMemInfo, TestMapsUsage) {
+ const std::vector<Vma>& maps = proc_mem->Maps();
+ ASSERT_FALSE(maps.empty());
+ ASSERT_EQ(proc->num_maps, maps.size());
+
+ pm_memusage_t map_usage, proc_usage;
+ pm_memusage_zero(&map_usage);
+ pm_memusage_zero(&proc_usage);
+ for (size_t i = 0; i < maps.size(); i++) {
+ ASSERT_EQ(0, pm_map_usage(proc->maps[i], &map_usage));
+ EXPECT_EQ(map_usage.vss, maps[i].usage.vss) << "VSS mismatch for map: " << maps[i].name;
+ EXPECT_EQ(map_usage.rss, maps[i].usage.rss) << "RSS mismatch for map: " << maps[i].name;
+ EXPECT_EQ(map_usage.pss, maps[i].usage.pss) << "PSS mismatch for map: " << maps[i].name;
+ EXPECT_EQ(map_usage.uss, maps[i].usage.uss) << "USS mismatch for map: " << maps[i].name;
+ pm_memusage_add(&proc_usage, &map_usage);
+ }
+
+ EXPECT_EQ(proc_usage.vss, proc_mem->Usage().vss);
+ EXPECT_EQ(proc_usage.rss, proc_mem->Usage().rss);
+ EXPECT_EQ(proc_usage.pss, proc_mem->Usage().pss);
+ EXPECT_EQ(proc_usage.uss, proc_mem->Usage().uss);
+}
+
+class ValidateProcMemInfoWss : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ASSERT_EQ(0, pm_kernel_create(&ker));
+ ASSERT_EQ(0, pm_process_create(ker, pid, &proc));
+ proc_mem = new ProcMemInfo(pid, true);
+ ASSERT_NE(proc_mem, nullptr);
+ }
+
+ void TearDown() override {
+ delete proc_mem;
+ pm_process_destroy(proc);
+ pm_kernel_destroy(ker);
+ }
+
+ pm_kernel_t* ker;
+ pm_process_t* proc;
+ ProcMemInfo* proc_mem;
+};
+
+TEST_F(ValidateProcMemInfoWss, TestWorkingTestReset) {
+ // Expect reset to succeed
+ EXPECT_TRUE(proc_mem->WssReset());
+}
+
+TEST_F(ValidateProcMemInfoWss, TestWssEquality) {
+ // Read wss using libpagemap
+ pm_memusage_t wss_pagemap;
+ EXPECT_EQ(0, pm_process_workingset(proc, &wss_pagemap, 0));
+
+ // Read wss using libmeminfo
+ MemUsage wss = proc_mem->Wss();
+
+ // compare
+ EXPECT_EQ(wss_pagemap.rss, wss.rss);
+ EXPECT_EQ(wss_pagemap.pss, wss.pss);
+ EXPECT_EQ(wss_pagemap.uss, wss.uss);
+}
+
+class ValidatePageAcct : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ASSERT_EQ(0, pm_kernel_create(&ker));
+ ASSERT_EQ(0, pm_process_create(ker, pid, &proc));
+ }
+
+ void TearDown() override {
+ pm_process_destroy(proc);
+ pm_kernel_destroy(ker);
+ }
+
+ pm_kernel_t* ker;
+ pm_process_t* proc;
+};
+
+TEST_F(ValidatePageAcct, TestPageFlags) {
+ PageAcct& pi = PageAcct::Instance();
+ pi.InitPageAcct(false);
+
+ uint64_t* pagemap;
+ size_t num_pages;
+ for (size_t i = 0; i < proc->num_maps; i++) {
+ ASSERT_EQ(0, pm_map_pagemap(proc->maps[i], &pagemap, &num_pages));
+ for (size_t j = 0; j < num_pages; j++) {
+ if (!PM_PAGEMAP_PRESENT(pagemap[j])) continue;
+
+ uint64_t pfn = PM_PAGEMAP_PFN(pagemap[j]);
+ uint64_t page_flags_pagemap, page_flags_meminfo;
+
+ ASSERT_EQ(0, pm_kernel_flags(ker, pfn, &page_flags_pagemap));
+ ASSERT_TRUE(pi.PageFlags(pfn, &page_flags_meminfo));
+ // check if page flags equal
+ EXPECT_EQ(page_flags_pagemap, page_flags_meminfo);
+ }
+ free(pagemap);
+ }
+}
+
+TEST_F(ValidatePageAcct, TestPageCounts) {
+ PageAcct& pi = PageAcct::Instance();
+ pi.InitPageAcct(false);
+
+ uint64_t* pagemap;
+ size_t num_pages;
+ for (size_t i = 0; i < proc->num_maps; i++) {
+ ASSERT_EQ(0, pm_map_pagemap(proc->maps[i], &pagemap, &num_pages));
+ for (size_t j = 0; j < num_pages; j++) {
+ uint64_t pfn = PM_PAGEMAP_PFN(pagemap[j]);
+ uint64_t map_count_pagemap, map_count_meminfo;
+
+ ASSERT_EQ(0, pm_kernel_count(ker, pfn, &map_count_pagemap));
+ ASSERT_TRUE(pi.PageMapCount(pfn, &map_count_meminfo));
+ // check if map counts are equal
+ EXPECT_EQ(map_count_pagemap, map_count_meminfo);
+ }
+ free(pagemap);
+ }
+}
+
+TEST_F(ValidatePageAcct, TestPageIdle) {
+ // skip the test if idle page tracking isn't enabled
+ if (pm_kernel_init_page_idle(ker) != 0) {
+ return;
+ }
+
+ PageAcct& pi = PageAcct::Instance();
+ ASSERT_TRUE(pi.InitPageAcct(true));
+
+ uint64_t* pagemap;
+ size_t num_pages;
+ for (size_t i = 0; i < proc->num_maps; i++) {
+ ASSERT_EQ(0, pm_map_pagemap(proc->maps[i], &pagemap, &num_pages));
+ for (size_t j = 0; j < num_pages; j++) {
+ if (!PM_PAGEMAP_PRESENT(pagemap[j])) continue;
+ uint64_t pfn = PM_PAGEMAP_PFN(pagemap[j]);
+
+ ASSERT_EQ(0, pm_kernel_mark_page_idle(ker, &pfn, 1));
+ int idle_status_pagemap = pm_kernel_get_page_idle(ker, pfn);
+ int idle_status_meminfo = pi.IsPageIdle(pfn);
+ EXPECT_EQ(idle_status_pagemap, idle_status_meminfo);
+ }
+ free(pagemap);
+ }
+}
+
+TEST(SysMemInfoParser, TestSysMemInfoFile) {
+ std::string meminfo = R"meminfo(MemTotal: 3019740 kB
+MemFree: 1809728 kB
+MemAvailable: 2546560 kB
+Buffers: 54736 kB
+Cached: 776052 kB
+SwapCached: 0 kB
+Active: 445856 kB
+Inactive: 459092 kB
+Active(anon): 78492 kB
+Inactive(anon): 2240 kB
+Active(file): 367364 kB
+Inactive(file): 456852 kB
+Unevictable: 3096 kB
+Mlocked: 3096 kB
+SwapTotal: 0 kB
+SwapFree: 0 kB
+Dirty: 32 kB
+Writeback: 0 kB
+AnonPages: 74988 kB
+Mapped: 62624 kB
+Shmem: 4020 kB
+Slab: 86464 kB
+SReclaimable: 44432 kB
+SUnreclaim: 42032 kB
+KernelStack: 4880 kB
+PageTables: 2900 kB
+NFS_Unstable: 0 kB
+Bounce: 0 kB
+WritebackTmp: 0 kB
+CommitLimit: 1509868 kB
+Committed_AS: 80296 kB
+VmallocTotal: 263061440 kB
+VmallocUsed: 0 kB
+VmallocChunk: 0 kB
+AnonHugePages: 6144 kB
+ShmemHugePages: 0 kB
+ShmemPmdMapped: 0 kB
+CmaTotal: 131072 kB
+CmaFree: 130380 kB
+HugePages_Total: 0
+HugePages_Free: 0
+HugePages_Rsvd: 0
+HugePages_Surp: 0
+Hugepagesize: 2048 kB)meminfo";
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(::android::base::WriteStringToFd(meminfo, tf.fd));
+
+ SysMemInfo mi;
+ ASSERT_TRUE(mi.ReadMemInfo(tf.path));
+ EXPECT_EQ(mi.mem_total_kb(), 3019740);
+ EXPECT_EQ(mi.mem_page_tables_kb(), 2900);
+}
+
+TEST(SysMemInfoParser, TestEmptyFile) {
+ TemporaryFile tf;
+ std::string empty_string = "";
+ ASSERT_TRUE(tf.fd != -1);
+ ASSERT_TRUE(::android::base::WriteStringToFd(empty_string, tf.fd));
+
+ SysMemInfo mi;
+ EXPECT_TRUE(mi.ReadMemInfo(tf.path));
+ EXPECT_EQ(mi.mem_total_kb(), 0);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ if (argc <= 1) {
+ cerr << "Pid of a permanently sleeping process must be provided." << endl;
+ exit(EXIT_FAILURE);
+ }
+ ::android::base::InitLogging(argv, android::base::StderrLogger);
+ pid = std::stoi(std::string(argv[1]));
+ return RUN_ALL_TESTS();
+}
diff --git a/libmeminfo/meminfo_private.h b/libmeminfo/meminfo_private.h
new file mode 100644
index 0000000..df5699c
--- /dev/null
+++ b/libmeminfo/meminfo_private.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <meminfo/meminfo.h>
+#include <meminfo/pageacct.h>
+#include <meminfo/procmeminfo.h>
+#include <meminfo/sysmeminfo.h>
+
+// Macros to do per-page flag manipulation
+#define _BITS(x, offset, bits) (((x) >> (offset)) & ((1LL << (bits)) - 1))
+#define PAGE_PRESENT(x) (_BITS(x, 63, 1))
+#define PAGE_SWAPPED(x) (_BITS(x, 62, 1))
+#define PAGE_SHIFT(x) (_BITS(x, 55, 6))
+#define PAGE_PFN(x) (_BITS(x, 0, 55))
+#define PAGE_SWAP_OFFSET(x) (_BITS(x, 5, 50))
+#define PAGE_SWAP_TYPE(x) (_BITS(x, 0, 5))
diff --git a/libmeminfo/pageacct.cpp b/libmeminfo/pageacct.cpp
new file mode 100644
index 0000000..887a74d
--- /dev/null
+++ b/libmeminfo/pageacct.cpp
@@ -0,0 +1,142 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include "meminfo_private.h"
+
+using unique_fd = ::android::base::unique_fd;
+
+namespace android {
+namespace meminfo {
+
+static inline off64_t pfn_to_idle_bitmap_offset(uint64_t pfn) {
+ return static_cast<off64_t>((pfn >> 6) << 3);
+}
+
+uint64_t pagesize(void) {
+ static uint64_t pagesize = sysconf(_SC_PAGE_SIZE);
+ return pagesize;
+}
+
+bool PageAcct::InitPageAcct(bool pageidle_enable) {
+ if (pageidle_enable && !PageAcct::KernelHasPageIdle()) {
+ LOG(ERROR) << "Idle page tracking is not supported by the kernel";
+ return false;
+ }
+
+ if (kpagecount_fd_ < 0) {
+ unique_fd count_fd(TEMP_FAILURE_RETRY(open("/proc/kpagecount", O_RDONLY | O_CLOEXEC)));
+ if (count_fd < 0) {
+ PLOG(ERROR) << "Failed to open /proc/kpagecount";
+ return false;
+ }
+ kpagecount_fd_ = std::move(count_fd);
+ }
+
+ if (kpageflags_fd_ < 0) {
+ unique_fd flags_fd(TEMP_FAILURE_RETRY(open("/proc/kpageflags", O_RDONLY | O_CLOEXEC)));
+ if (flags_fd < 0) {
+ PLOG(ERROR) << "Failed to open /proc/kpageflags";
+ return false;
+ }
+ kpageflags_fd_ = std::move(flags_fd);
+ }
+
+ if (pageidle_enable && pageidle_fd_ < 0) {
+ unique_fd idle_fd(
+ TEMP_FAILURE_RETRY(open("/sys/kernel/mm/page_idle/bitmap", O_RDWR | O_CLOEXEC)));
+ if (idle_fd < 0) {
+ PLOG(ERROR) << "Failed to open page idle bitmap";
+ return false;
+ }
+ pageidle_fd_ = std::move(idle_fd);
+ }
+
+ return true;
+}
+
+bool PageAcct::PageFlags(uint64_t pfn, uint64_t* flags) {
+ if (!flags) return false;
+
+ if (kpageflags_fd_ < 0) {
+ if (!InitPageAcct()) return false;
+ }
+
+ if (pread64(kpageflags_fd_, flags, sizeof(uint64_t), pfn * sizeof(uint64_t)) < 0) {
+ PLOG(ERROR) << "Failed to read page flags for page " << pfn;
+ return false;
+ }
+ return true;
+}
+
+bool PageAcct::PageMapCount(uint64_t pfn, uint64_t* mapcount) {
+ if (!mapcount) return false;
+
+ if (kpagecount_fd_ < 0) {
+ if (!InitPageAcct()) return false;
+ }
+
+ if (pread64(kpagecount_fd_, mapcount, sizeof(uint64_t), pfn * sizeof(uint64_t)) < 0) {
+ PLOG(ERROR) << "Failed to read map count for page " << pfn;
+ return false;
+ }
+ return true;
+}
+
+int PageAcct::IsPageIdle(uint64_t pfn) {
+ if (pageidle_fd_ < 0) {
+ if (!InitPageAcct(true)) return -EOPNOTSUPP;
+ }
+
+ int idle_status = MarkPageIdle(pfn);
+ if (idle_status) return idle_status;
+
+ return GetPageIdle(pfn);
+}
+
+int PageAcct::MarkPageIdle(uint64_t pfn) const {
+ off64_t offset = pfn_to_idle_bitmap_offset(pfn);
+ // set the bit corresponding to page frame
+ uint64_t idle_bits = 1ULL << (pfn % 64);
+
+ if (pwrite64(pageidle_fd_, &idle_bits, sizeof(uint64_t), offset) < 0) {
+ PLOG(ERROR) << "Failed to write page idle bitmap for page " << pfn;
+ return -errno;
+ }
+
+ return 0;
+}
+
+int PageAcct::GetPageIdle(uint64_t pfn) const {
+ off64_t offset = pfn_to_idle_bitmap_offset(pfn);
+ uint64_t idle_bits;
+
+ if (pread64(pageidle_fd_, &idle_bits, sizeof(uint64_t), offset) < 0) {
+ PLOG(ERROR) << "Failed to read page idle bitmap for page " << pfn;
+ return -errno;
+ }
+
+ return !!(idle_bits & (1ULL << (pfn % 64)));
+}
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/procmeminfo.cpp b/libmeminfo/procmeminfo.cpp
new file mode 100644
index 0000000..fe91d25
--- /dev/null
+++ b/libmeminfo/procmeminfo.cpp
@@ -0,0 +1,218 @@
+/*
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <linux/kernel-page-flags.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <procinfo/process_map.h>
+
+#include "meminfo_private.h"
+
+namespace android {
+namespace meminfo {
+
+static void add_mem_usage(MemUsage* to, const MemUsage& from) {
+ to->vss += from.vss;
+ to->rss += from.rss;
+ to->pss += from.pss;
+ to->uss += from.uss;
+
+ to->private_clean += from.private_clean;
+ to->private_dirty += from.private_dirty;
+
+ to->shared_clean += from.shared_clean;
+ to->shared_dirty += from.shared_dirty;
+}
+
+ProcMemInfo::ProcMemInfo(pid_t pid, bool get_wss) : pid_(pid), get_wss_(get_wss) {
+ if (!ReadMaps(get_wss_)) {
+ LOG(ERROR) << "Failed to read maps for Process " << pid_;
+ }
+}
+
+const std::vector<Vma>& ProcMemInfo::Maps() {
+ return maps_;
+}
+
+const MemUsage& ProcMemInfo::Usage() {
+ if (get_wss_) {
+ LOG(WARNING) << "Trying to read memory usage from working set object";
+ }
+ return usage_;
+}
+
+const MemUsage& ProcMemInfo::Wss() {
+ if (!get_wss_) {
+ LOG(WARNING) << "Trying to read working set when there is none";
+ }
+
+ return wss_;
+}
+
+bool ProcMemInfo::WssReset() {
+ if (!get_wss_) {
+ LOG(ERROR) << "Trying to reset working set from a memory usage counting object";
+ return false;
+ }
+
+ std::string clear_refs_path = ::android::base::StringPrintf("/proc/%d/clear_refs", pid_);
+ if (!::android::base::WriteStringToFile("1\n", clear_refs_path)) {
+ PLOG(ERROR) << "Failed to write to " << clear_refs_path;
+ return false;
+ }
+
+ wss_.clear();
+ return true;
+}
+
+bool ProcMemInfo::ReadMaps(bool get_wss) {
+ // parse and read /proc/<pid>/maps
+ std::string maps_file = ::android::base::StringPrintf("/proc/%d/maps", pid_);
+ if (!::android::procinfo::ReadMapFile(
+ maps_file, [&](uint64_t start, uint64_t end, uint16_t flags, uint64_t pgoff,
+ const char* name) {
+ maps_.emplace_back(Vma(start, end, pgoff, flags, name));
+ })) {
+ LOG(ERROR) << "Failed to parse " << maps_file;
+ maps_.clear();
+ return false;
+ }
+
+ std::string pagemap_file = ::android::base::StringPrintf("/proc/%d/pagemap", pid_);
+ ::android::base::unique_fd pagemap_fd(
+ TEMP_FAILURE_RETRY(open(pagemap_file.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (pagemap_fd < 0) {
+ PLOG(ERROR) << "Failed to open " << pagemap_file;
+ return false;
+ }
+
+ for (auto& vma : maps_) {
+ if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss)) {
+ LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start
+ << "-" << vma.end << "]";
+ maps_.clear();
+ return false;
+ }
+ if (get_wss) {
+ add_mem_usage(&wss_, vma.wss);
+ } else {
+ add_mem_usage(&usage_, vma.usage);
+ }
+ }
+
+ return true;
+}
+
+bool ProcMemInfo::ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss) {
+ PageAcct& pinfo = PageAcct::Instance();
+ uint64_t pagesz = getpagesize();
+ uint64_t num_pages = (vma.end - vma.start) / pagesz;
+
+ std::unique_ptr<uint64_t[]> pg_frames(new uint64_t[num_pages]);
+ uint64_t first = vma.start / pagesz;
+ if (pread64(pagemap_fd, pg_frames.get(), num_pages * sizeof(uint64_t),
+ first * sizeof(uint64_t)) < 0) {
+ PLOG(ERROR) << "Failed to read page frames from page map for pid: " << pid_;
+ return false;
+ }
+
+ std::unique_ptr<uint64_t[]> pg_flags(new uint64_t[num_pages]);
+ std::unique_ptr<uint64_t[]> pg_counts(new uint64_t[num_pages]);
+ for (uint64_t i = 0; i < num_pages; ++i) {
+ if (!get_wss) {
+ vma.usage.vss += pagesz;
+ }
+ uint64_t p = pg_frames[i];
+ if (!PAGE_PRESENT(p) && !PAGE_SWAPPED(p)) continue;
+
+ if (PAGE_SWAPPED(p)) {
+ // TODO: do what's needed for swapped pages
+ continue;
+ }
+
+ uint64_t page_frame = PAGE_PFN(p);
+ if (!pinfo.PageFlags(page_frame, &pg_flags[i])) {
+ LOG(ERROR) << "Failed to get page flags for " << page_frame << " in process " << pid_;
+ return false;
+ }
+
+ if (!pinfo.PageMapCount(page_frame, &pg_counts[i])) {
+ LOG(ERROR) << "Failed to get page count for " << page_frame << " in process " << pid_;
+ return false;
+ }
+
+ // Page was unmapped between the presence check at the beginning of the loop and here.
+ if (pg_counts[i] == 0) {
+ pg_frames[i] = 0;
+ pg_flags[i] = 0;
+ continue;
+ }
+
+ bool is_dirty = !!(pg_flags[i] & (1 << KPF_DIRTY));
+ bool is_private = (pg_counts[i] == 1);
+ // Working set
+ if (get_wss) {
+ bool is_referenced = !!(pg_flags[i] & (1 << KPF_REFERENCED));
+ if (!is_referenced) {
+ continue;
+ }
+ // This effectively makes vss = rss for the working set is requested.
+ // The libpagemap implementation returns vss > rss for
+ // working set, which doesn't make sense.
+ vma.wss.vss += pagesz;
+ vma.wss.rss += pagesz;
+ vma.wss.uss += is_private ? pagesz : 0;
+ vma.wss.pss += pagesz / pg_counts[i];
+ if (is_private) {
+ vma.wss.private_dirty += is_dirty ? pagesz : 0;
+ vma.wss.private_clean += is_dirty ? 0 : pagesz;
+ } else {
+ vma.wss.shared_dirty += is_dirty ? pagesz : 0;
+ vma.wss.shared_clean += is_dirty ? 0 : pagesz;
+ }
+ } else {
+ vma.usage.rss += pagesz;
+ vma.usage.uss += is_private ? pagesz : 0;
+ vma.usage.pss += pagesz / pg_counts[i];
+ if (is_private) {
+ vma.usage.private_dirty += is_dirty ? pagesz : 0;
+ vma.usage.private_clean += is_dirty ? 0 : pagesz;
+ } else {
+ vma.usage.shared_dirty += is_dirty ? pagesz : 0;
+ vma.usage.shared_clean += is_dirty ? 0 : pagesz;
+ }
+ }
+ }
+
+ return true;
+}
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/sysmeminfo.cpp b/libmeminfo/sysmeminfo.cpp
new file mode 100644
index 0000000..50fa213
--- /dev/null
+++ b/libmeminfo/sysmeminfo.cpp
@@ -0,0 +1,129 @@
+/*
+ * 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 <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <cctype>
+#include <fstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+#include "meminfo_private.h"
+
+namespace android {
+namespace meminfo {
+
+const std::vector<std::string> SysMemInfo::kDefaultSysMemInfoTags = {
+ "MemTotal:", "MemFree:", "Buffers:", "Cached:", "Shmem:",
+ "Slab:", "SReclaimable:", "SUnreclaim:", "SwapTotal:", "SwapFree:",
+ "ZRam:", "Mapped:", "VmallocUsed:", "PageTables:", "KernelStack:",
+};
+
+bool SysMemInfo::ReadMemInfo(const std::string& path) {
+ return ReadMemInfo(SysMemInfo::kDefaultSysMemInfoTags, path);
+}
+
+// TODO: Delete this function if it can't match up with the c-like implementation below.
+// Currently, this added about 50 % extra overhead on hikey.
+#if 0
+bool SysMemInfo::ReadMemInfo(const std::vector<std::string>& tags, const std::string& path) {
+ std::string buffer;
+ if (!::android::base::ReadFileToString(path, &buffer)) {
+ PLOG(ERROR) << "Failed to read : " << path;
+ return false;
+ }
+
+ uint32_t total_found = 0;
+ for (auto s = buffer.begin(); s < buffer.end() && total_found < tags.size();) {
+ for (auto& tag : tags) {
+ if (tag == std::string(s, s + tag.size())) {
+ s += tag.size();
+ while (isspace(*s)) s++;
+ auto num_start = s;
+ while (std::isdigit(*s)) s++;
+
+ std::string number(num_start, num_start + (s - num_start));
+ if (!::android::base::ParseUint(number, &mem_in_kb_[tag])) {
+ LOG(ERROR) << "Failed to parse uint";
+ return false;
+ }
+ total_found++;
+ break;
+ }
+ }
+ while (s < buffer.end() && *s != '\n') s++;
+ if (s < buffer.end()) s++;
+ }
+
+ return true;
+}
+
+#else
+bool SysMemInfo::ReadMemInfo(const std::vector<std::string>& tags, const std::string& path) {
+ char buffer[4096];
+ int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd < 0) {
+ PLOG(ERROR) << "Failed to open file :" << path;
+ return false;
+ }
+
+ const int len = read(fd, buffer, sizeof(buffer) - 1);
+ close(fd);
+ if (len < 0) {
+ return false;
+ }
+
+ buffer[len] = '\0';
+ char* p = buffer;
+ uint32_t found = 0;
+ while (*p && found < tags.size()) {
+ for (auto& tag : tags) {
+ if (strncmp(p, tag.c_str(), tag.size()) == 0) {
+ p += tag.size();
+ while (*p == ' ') p++;
+ char* endptr = nullptr;
+ mem_in_kb_[tag] = strtoull(p, &endptr, 10);
+ if (p == endptr) {
+ PLOG(ERROR) << "Failed to parse line in file: " << path;
+ return false;
+ }
+ p = endptr;
+ found++;
+ break;
+ }
+ }
+ while (*p && *p != '\n') {
+ p++;
+ }
+ if (*p) p++;
+ }
+
+ return true;
+}
+#endif
+
+} // namespace meminfo
+} // namespace android
diff --git a/libmeminfo/tools/Android.bp b/libmeminfo/tools/Android.bp
new file mode 100644
index 0000000..0870130
--- /dev/null
+++ b/libmeminfo/tools/Android.bp
@@ -0,0 +1,27 @@
+// 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.
+
+cc_binary {
+ name: "procmem2",
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ ],
+
+ srcs: ["procmem.cpp"],
+ shared_libs: [
+ "libmeminfo",
+ ],
+}
diff --git a/libmeminfo/tools/procmem.cpp b/libmeminfo/tools/procmem.cpp
new file mode 100644
index 0000000..3571e41
--- /dev/null
+++ b/libmeminfo/tools/procmem.cpp
@@ -0,0 +1,171 @@
+/*
+ * 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 <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <meminfo/procmeminfo.h>
+
+using ProcMemInfo = ::android::meminfo::ProcMemInfo;
+using MemUsage = ::android::meminfo::MemUsage;
+
+static void usage(const char* cmd) {
+ fprintf(stderr,
+ "Usage: %s [-i] [ -w | -W ] [ -p | -m ] [ -h ] pid\n"
+ " -i Uses idle page tracking for working set statistics.\n"
+ " -w Displays statistics for the working set only.\n"
+ " -W Resets the working set of the process.\n"
+ " -p Sort by PSS.\n"
+ " -u Sort by USS.\n"
+ " -m Sort by mapping order (as read from /proc).\n"
+ " -h Hide maps with no RSS.\n",
+ cmd);
+}
+
+static void show_footer(uint32_t nelem, const std::string& padding) {
+ std::string elem(7, '-');
+
+ for (uint32_t i = 0; i < nelem; ++i) {
+ std::cout << std::setw(7) << elem << padding;
+ }
+ std::cout << std::endl;
+}
+
+static void show_header(const std::vector<std::string>& header, const std::string& padding) {
+ if (header.empty()) return;
+
+ for (size_t i = 0; i < header.size() - 1; ++i) {
+ std::cout << std::setw(7) << header[i] << padding;
+ }
+ std::cout << header.back() << std::endl;
+ show_footer(header.size() - 1, padding);
+}
+
+static void scan_usage(std::stringstream& ss, const MemUsage& usage, const std::string& padding,
+ bool show_wss) {
+ // clear string stream first.
+ ss.str("");
+ // TODO: use ::android::base::StringPrintf instead of <iomanip> here.
+ if (!show_wss)
+ ss << std::setw(6) << usage.vss/1024 << padding;
+ ss << std::setw(6) << usage.rss/1024 << padding << std::setw(6)
+ << usage.pss/1024 << padding << std::setw(6) << usage.uss/1024 << padding
+ << std::setw(6) << usage.shared_clean/1024 << padding << std::setw(6)
+ << usage.shared_dirty/1024 << padding << std::setw(6)
+ << usage.private_clean/1024 << padding << std::setw(6)
+ << usage.private_dirty/1024 << padding;
+}
+
+static int show(ProcMemInfo& proc, bool hide_zeroes, bool show_wss) {
+ const std::vector<std::string> main_header = {"Vss", "Rss", "Pss", "Uss", "ShCl",
+ "ShDi", "PrCl", "PrDi", "Name"};
+ const std::vector<std::string> wss_header = {"WRss", "WPss", "WUss", "WShCl",
+ "WShDi", "WPrCl", "WPrDi", "Name"};
+ const std::vector<std::string>& header = show_wss ? wss_header : main_header;
+
+ // Read process memory stats
+ const MemUsage& stats = show_wss ? proc.Wss() : proc.Usage();
+ const std::vector<::android::meminfo::Vma>& maps = proc.Maps();
+
+ // following retains 'procmem' output so as to not break any scripts
+ // that rely on it.
+ std::string spaces = " ";
+ show_header(header, spaces);
+ const std::string padding = "K ";
+ std::stringstream ss;
+ for (auto& vma : maps) {
+ const MemUsage& vma_stats = show_wss ? vma.wss : vma.usage;
+ if (hide_zeroes && vma_stats.rss == 0) {
+ continue;
+ }
+ scan_usage(ss, vma_stats, padding, show_wss);
+ ss << vma.name << std::endl;
+ std::cout << ss.str();
+ }
+ show_footer(header.size() - 1, spaces);
+ scan_usage(ss, stats, padding, show_wss);
+ ss << "TOTAL" << std::endl;
+ std::cout << ss.str();
+
+ return 0;
+}
+
+int main(int argc, char* argv[]) {
+ bool use_pageidle = false;
+ bool hide_zeroes = false;
+ bool wss_reset = false;
+ bool show_wss = false;
+ int opt;
+
+ while ((opt = getopt(argc, argv, "himpuWw")) != -1) {
+ switch (opt) {
+ case 'h':
+ hide_zeroes = true;
+ break;
+ case 'i':
+ use_pageidle = true;
+ break;
+ case 'm':
+ break;
+ case 'p':
+ break;
+ case 'u':
+ break;
+ case 'W':
+ wss_reset = true;
+ break;
+ case 'w':
+ show_wss = true;
+ break;
+ case '?':
+ usage(argv[0]);
+ return 0;
+ default:
+ abort();
+ }
+ }
+
+ if (optind != (argc - 1)) {
+ fprintf(stderr, "Need exactly one pid at the end\n");
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ pid_t pid = atoi(argv[optind]);
+ if (pid == 0) {
+ std::cerr << "Invalid process id" << std::endl;
+ exit(EXIT_FAILURE);
+ }
+
+ bool need_wss = wss_reset || show_wss;
+ ProcMemInfo proc(pid, need_wss);
+ if (wss_reset) {
+ if (!proc.WssReset()) {
+ std::cerr << "Failed to reset working set of pid : " << pid << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ return 0;
+ }
+
+ return show(proc, hide_zeroes, show_wss);
+}
diff --git a/libmetricslogger/Android.bp b/libmetricslogger/Android.bp
index 2d327ee..7d7554b 100644
--- a/libmetricslogger/Android.bp
+++ b/libmetricslogger/Android.bp
@@ -34,15 +34,6 @@
export_shared_lib_headers: ["libstatssocket"],
}
-// static version of libmetricslogger, needed by a few art static binaries
-// TODO(b/117829226): Remove once dependencies are cleaned up.
-cc_library_static {
- name: "libmetricslogger_static",
- srcs: metricslogger_lib_src_files,
- defaults: ["metricslogger_defaults"],
- export_shared_lib_headers: ["libstatssocket"],
-}
-
// metricslogger shared library, debug
// -----------------------------------------------------------------------------
cc_library_shared {
diff --git a/libutils/include/utils/Thread.h b/libutils/include/utils/Thread.h
index 3525138..fc67656 100644
--- a/libutils/include/utils/Thread.h
+++ b/libutils/include/utils/Thread.h
@@ -47,6 +47,7 @@
virtual ~Thread();
// Start the thread in threadLoop() which needs to be implemented.
+ // NOLINTNEXTLINE(google-default-arguments)
virtual status_t run( const char* name,
int32_t priority = PRIORITY_DEFAULT,
size_t stack = 0);
diff --git a/llkd/README.md b/llkd/README.md
index e5be850..3da7a2f 100644
--- a/llkd/README.md
+++ b/llkd/README.md
@@ -44,7 +44,8 @@
ABA detection since forward scheduling progress is allowed, thus the condition
for the symbols are:
-- Check is looking for " " + __symbol__+ "0x" in /proc/<pid>/stack.
+- Check is looking for " __symbol__+0x" or " __symbol__.cfi+0x" in
+ /proc/__pid__/stack.
- The __symbol__ should be rare and short lived enough that on a typical
system the function is seen at most only once in a sample over the timeout
period of ro.llk.stack.timeout_ms, samples occur every ro.llk.check_ms. This
@@ -88,7 +89,14 @@
Android Properties llkd respond to (*prop*_ms parms are in milliseconds):
#### ro.config.low_ram
-default false, if true do not sysrq t (dump all threads).
+device is configured with limited memory.
+
+#### ro.debuggable
+device is configured for userdebug or eng build.
+
+#### ro.llk.sysrq_t
+default not ro.config.low_ram, or ro.debuggable if property is "eng".
+if true do sysrq t (dump all threads).
#### ro.llk.enable
default false, allow live-lock daemon to be enabled.
@@ -121,14 +129,14 @@
#### ro.llk.stack.timeout_ms
default ro.llk.timeout_ms,
checking for persistent stack symbols maximum timelimit.
-Only active on userdebug and eng builds.
+Only active on userdebug or eng builds.
#### ro.llk.check_ms
default 2 minutes samples of threads for D or Z.
#### ro.llk.stack
-default cma_alloc,__get_user_pages, comma separated list of kernel symbols.
-The string "*false*" is the equivalent to an *empty* list.
+default cma_alloc,__get_user_pages,bit_wait_io comma separated list of kernel
+symbols. The string "*false*" is the equivalent to an *empty* list.
Look for kernel stack symbols that if ever persistently present can
indicate a subsystem is locked up.
Beware, check does not on purpose do forward scheduling ABA except by polling
@@ -136,11 +144,14 @@
should be exceptionally rare and fleeting.
One must be convinced that it is virtually *impossible* for symbol to show up
persistently in all samples of the stack.
-Only active on userdebug and eng builds.
+Again, looks for a match for either " **symbol**+0x" or " **symbol**.cfi+0x"
+in stack expansion.
+Only available on userdebug or eng builds, limited privileges due to security
+concerns on user builds prevents this checking.
#### ro.llk.blacklist.process
default 0,1,2 (kernel, init and [kthreadd]) plus process names
-init,[kthreadd],[khungtaskd],lmkd,lmkd.llkd,llkd,watchdogd,
+init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,
[watchdogd],[watchdogd/0],...,[watchdogd/***get_nprocs**-1*].
The string "*false*" is the equivalent to an *empty* list.
Do not watch these processes. A process can be comm, cmdline or pid reference.
@@ -160,7 +171,7 @@
Do not watch processes that match this uid.
#### ro.llk.blacklist.process.stack
-default process names init,lmkd,lmkd.llkd,llkd,keystore,logd.
+default process names init,lmkd.llkd,llkd,keystore,ueventd,apexd,logd.
The string "*false*" is the equivalent to an *empty* list.
This subset of processes are not monitored for live lock stack signatures.
Also prevents the sepolicy violation associated with processes that block
diff --git a/llkd/include/llkd.h b/llkd/include/llkd.h
index 2c62fca..b16b1d8 100644
--- a/llkd/include/llkd.h
+++ b/llkd/include/llkd.h
@@ -35,6 +35,8 @@
#define LLK_ENABLE_DEFAULT false /* "eng" and userdebug true */
#define KHT_ENABLE_WRITEABLE_PROPERTY "khungtask.enable"
#define KHT_ENABLE_PROPERTY "ro." KHT_ENABLE_WRITEABLE_PROPERTY
+#define LLK_ENABLE_SYSRQ_T_PROPERTY "ro.llk.sysrq_t"
+#define LLK_ENABLE_SYSRQ_T_DEFAULT true
#define LLK_MLOCKALL_PROPERTY "ro.llk.mlockall"
#define LLK_MLOCKALL_DEFAULT true
#define LLK_KILLTEST_PROPERTY "ro.llk.killtest"
@@ -48,7 +50,7 @@
/* LLK_CHECK_MS_DEFAULT = actual timeout_ms / LLK_CHECKS_PER_TIMEOUT_DEFAULT */
#define LLK_CHECKS_PER_TIMEOUT_DEFAULT 5
#define LLK_CHECK_STACK_PROPERTY "ro.llk.stack"
-#define LLK_CHECK_STACK_DEFAULT "cma_alloc,__get_user_pages"
+#define LLK_CHECK_STACK_DEFAULT "cma_alloc,__get_user_pages,bit_wait_io"
#define LLK_BLACKLIST_PROCESS_PROPERTY "ro.llk.blacklist.process"
#define LLK_BLACKLIST_PROCESS_DEFAULT \
"0,1,2,init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,[watchdogd],[watchdogd/0]"
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 2727aab..0827470 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -85,6 +85,7 @@
milliseconds llkCheckMs; // checking interval to inspect any
// persistent live-locked states
bool llkLowRam; // ro.config.low_ram
+bool llkEnableSysrqT = LLK_ENABLE_SYSRQ_T_DEFAULT; // sysrq stack trace dump
bool khtEnable = LLK_ENABLE_DEFAULT; // [khungtaskd] panic
// [khungtaskd] should have a timeout beyond the granularity of llkTimeoutMs.
// Provides a wide angle of margin b/c khtTimeout is also its granularity.
@@ -509,8 +510,10 @@
return android::base::Trim(content) == string;
}
-void llkPanicKernel(bool dump, pid_t tid, const char* state) __noreturn;
-void llkPanicKernel(bool dump, pid_t tid, const char* state) {
+void llkPanicKernel(bool dump, pid_t tid, const char* state,
+ const std::string& message = "") __noreturn;
+void llkPanicKernel(bool dump, pid_t tid, const char* state, const std::string& message) {
+ if (!message.empty()) LOG(ERROR) << message;
auto sysrqTriggerFd = llkFileToWriteFd("/proc/sysrq-trigger");
if (sysrqTriggerFd < 0) {
// DYB
@@ -523,14 +526,24 @@
if (dump) {
// Show all locks that are held
android::base::WriteStringToFd("d", sysrqTriggerFd);
+ // Show all waiting tasks
+ android::base::WriteStringToFd("w", sysrqTriggerFd);
// This can trigger hardware watchdog, that is somewhat _ok_.
// But useless if pstore configured for <256KB, low ram devices ...
- if (!llkLowRam) {
+ if (llkEnableSysrqT) {
android::base::WriteStringToFd("t", sysrqTriggerFd);
+ // Show all locks that are held (in case 't' overflows ramoops)
+ android::base::WriteStringToFd("d", sysrqTriggerFd);
+ // Show all waiting tasks (in case 't' overflows ramoops)
+ android::base::WriteStringToFd("w", sysrqTriggerFd);
}
::usleep(200000); // let everything settle
}
- llkWriteStringToFile("SysRq : Trigger a crash : 'livelock,"s + state + "'\n", "/dev/kmsg");
+ // SysRq message matches kernel format, and propagates through bootstat
+ // ultimately to the boot reason into panic,livelock,<state>.
+ llkWriteStringToFile(message + (message.empty() ? "" : "\n") +
+ "SysRq : Trigger a crash : 'livelock,"s + state + "'\n",
+ "/dev/kmsg");
android::base::WriteStringToFd("c", sysrqTriggerFd);
// NOTREACHED
// DYB
@@ -726,7 +739,8 @@
char match = -1;
for (const auto& stack : llkCheckStackSymbols) {
if (++idx < 0) break;
- if (kernel_stack.find(" "s + stack + "+0x") != std::string::npos) {
+ if ((kernel_stack.find(" "s + stack + "+0x") != std::string::npos) ||
+ (kernel_stack.find(" "s + stack + ".cfi+0x") != std::string::npos)) {
match = idx;
break;
}
@@ -798,6 +812,7 @@
void llkLogConfig(void) {
LOG(INFO) << "ro.config.low_ram=" << llkFormat(llkLowRam) << "\n"
+ << LLK_ENABLE_SYSRQ_T_PROPERTY "=" << llkFormat(llkEnableSysrqT) << "\n"
<< LLK_ENABLE_PROPERTY "=" << llkFormat(llkEnable) << "\n"
<< KHT_ENABLE_PROPERTY "=" << llkFormat(khtEnable) << "\n"
<< LLK_MLOCKALL_PROPERTY "=" << llkFormat(llkMlockall) << "\n"
@@ -1089,10 +1104,12 @@
}
}
// We are here because we have confirmed kernel live-lock
- LOG(ERROR) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << "->" << pid
- << "->" << tid << ' ' << procp->getComm() << " [panic]";
+ const auto message = state + " "s + llkFormat(procp->count) + " " +
+ std::to_string(ppid) + "->" + std::to_string(pid) + "->" +
+ std::to_string(tid) + " " + procp->getComm() + " [panic]";
llkPanicKernel(true, tid,
- (state == 'Z') ? "zombie" : (state == 'D') ? "driver" : "sleeping");
+ (state == 'Z') ? "zombie" : (state == 'D') ? "driver" : "sleeping",
+ message);
}
LOG(VERBOSE) << "+closedir()";
}
@@ -1149,13 +1166,22 @@
return duration_cast<milliseconds>(llkCheck()).count();
}
+bool llkCheckEng(const std::string& property) {
+ return android::base::GetProperty(property, "eng") == "eng";
+}
+
bool llkInit(const char* threadname) {
auto debuggable = android::base::GetBoolProperty("ro.debuggable", false);
llkLowRam = android::base::GetBoolProperty("ro.config.low_ram", false);
- if (!LLK_ENABLE_DEFAULT && debuggable) {
- llkEnable = android::base::GetProperty(LLK_ENABLE_PROPERTY, "eng") == "eng";
- khtEnable = android::base::GetProperty(KHT_ENABLE_PROPERTY, "eng") == "eng";
+ llkEnableSysrqT &= !llkLowRam;
+ if (debuggable) {
+ llkEnableSysrqT |= llkCheckEng(LLK_ENABLE_SYSRQ_T_PROPERTY);
+ if (!LLK_ENABLE_DEFAULT) { // NB: default is currently true ...
+ llkEnable |= llkCheckEng(LLK_ENABLE_PROPERTY);
+ khtEnable |= llkCheckEng(KHT_ENABLE_PROPERTY);
+ }
}
+ llkEnableSysrqT = android::base::GetBoolProperty(LLK_ENABLE_SYSRQ_T_PROPERTY, llkEnableSysrqT);
llkEnable = android::base::GetBoolProperty(LLK_ENABLE_PROPERTY, llkEnable);
if (llkEnable && !llkTopDirectory.reset(procdir)) {
// Most likely reason we could be here is llkd was started
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index d005ecf..ac470d4 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -167,6 +167,15 @@
if (!packagelist_parse(packagelist_parse_callback, &info)) {
error(1, errno, "packagelist_parse failed");
}
+
+ // Handle a multi-user data path
+ if (userId > 0) {
+ free(info.data_dir);
+ if (asprintf(&info.data_dir, "/data/user/%d/%s", userId, pkgname) == -1) {
+ error(1, errno, "asprintf failed");
+ }
+ }
+
if (info.uid == 0) {
error(1, 0, "unknown package: %s", pkgname);
}