Merge "liblog: remove more endianness functions"
diff --git a/adb/test_device.py b/adb/test_device.py
index 32d797e..57925e8 100755
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -903,10 +903,12 @@
remote_path += '/filename'
self.device.push(local=tmp_file.name, remote=remote_path)
- def test_push_multiple_slash_root(self):
+ def disabled_test_push_multiple_slash_root(self):
"""Regression test for pushing to //data/local/tmp.
Bug: http://b/141311284
+
+ Disabled because this broken on the adbd side as well: b/141943968
"""
with tempfile.NamedTemporaryFile() as tmp_file:
tmp_file.write('\0' * 1024 * 1024)
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.cpp b/fs_mgr/libsnapshot/partition_cow_creator.cpp
index 4250d23..404ef27 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator.cpp
@@ -67,59 +67,6 @@
return false;
}
-// Return the number of sectors, N, where |target_partition|[0..N] (from
-// |target_metadata|) are the sectors that should be snapshotted. N is computed
-// so that this range of sectors are used by partitions in |current_metadata|.
-//
-// The client code (update_engine) should have computed target_metadata by
-// resizing partitions of current_metadata, so only the first N sectors should
-// be snapshotted, not a range with start index != 0.
-//
-// Note that if partition A has shrunk and partition B has grown, the new
-// extents of partition B may use the empty space that was used by partition A.
-// In this case, that new extent cannot be written directly, as it may be used
-// by the running system. Hence, all extents of the new partition B must be
-// intersected with all old partitions (including old partition A and B) to get
-// the region that needs to be snapshotted.
-std::optional<uint64_t> PartitionCowCreator::GetSnapshotSize() {
- // Compute the number of sectors that needs to be snapshotted.
- uint64_t snapshot_sectors = 0;
- std::vector<std::unique_ptr<Extent>> intersections;
- for (const auto& extent : target_partition->extents()) {
- for (auto* existing_partition :
- ListPartitionsWithSuffix(current_metadata, current_suffix)) {
- for (const auto& existing_extent : existing_partition->extents()) {
- auto intersection = Intersect(extent.get(), existing_extent.get());
- if (intersection != nullptr && intersection->num_sectors() > 0) {
- snapshot_sectors += intersection->num_sectors();
- intersections.emplace_back(std::move(intersection));
- }
- }
- }
- }
- uint64_t snapshot_size = snapshot_sectors * kSectorSize;
-
- // Sanity check that all recorded intersections are indeed within
- // target_partition[0..snapshot_sectors].
- Partition target_partition_snapshot = target_partition->GetBeginningExtents(snapshot_size);
- for (const auto& intersection : intersections) {
- if (!HasExtent(&target_partition_snapshot, intersection.get())) {
- auto linear_intersection = intersection->AsLinearExtent();
- LOG(ERROR) << "Extent "
- << (linear_intersection
- ? (std::to_string(linear_intersection->physical_sector()) + "," +
- std::to_string(linear_intersection->end_sector()))
- : "")
- << " is not part of Partition " << target_partition->name() << "[0.."
- << snapshot_size
- << "]. The metadata wasn't constructed correctly. This should not happen.";
- return std::nullopt;
- }
- }
-
- return snapshot_size;
-}
-
std::optional<uint64_t> PartitionCowCreator::GetCowSize(uint64_t snapshot_size) {
// TODO: Use |operations|. to determine a minimum COW size.
// kCowEstimateFactor is good for prototyping but we can't use that in production.
@@ -139,12 +86,11 @@
Return ret;
ret.snapshot_status.device_size = target_partition->size();
- auto snapshot_size = GetSnapshotSize();
- if (!snapshot_size.has_value()) return std::nullopt;
+ // TODO(b/141889746): Optimize by using a smaller snapshot. Some ranges in target_partition
+ // may be written directly.
+ ret.snapshot_status.snapshot_size = target_partition->size();
- ret.snapshot_status.snapshot_size = *snapshot_size;
-
- auto cow_size = GetCowSize(*snapshot_size);
+ auto cow_size = GetCowSize(ret.snapshot_status.snapshot_size);
if (!cow_size.has_value()) return std::nullopt;
// Compute regions that are free in both current and target metadata. These are the regions
diff --git a/fs_mgr/libsnapshot/partition_cow_creator.h b/fs_mgr/libsnapshot/partition_cow_creator.h
index 7c81418..0e645c6 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator.h
+++ b/fs_mgr/libsnapshot/partition_cow_creator.h
@@ -59,7 +59,6 @@
private:
bool HasExtent(Partition* p, Extent* e);
- std::optional<uint64_t> GetSnapshotSize();
std::optional<uint64_t> GetCowSize(uint64_t snapshot_size);
};
diff --git a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
index 4c9afff..ccd087e 100644
--- a/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
+++ b/fs_mgr/libsnapshot/partition_cow_creator_test.cpp
@@ -20,7 +20,7 @@
#include "partition_cow_creator.h"
#include "test_helpers.h"
-using ::android::fs_mgr::MetadataBuilder;
+using namespace android::fs_mgr;
namespace android {
namespace snapshot {
@@ -55,5 +55,46 @@
ASSERT_EQ(40 * 1024, ret->snapshot_status.snapshot_size);
}
+TEST_F(PartitionCowCreatorTest, Holes) {
+ const auto& opener = test_device->GetPartitionOpener();
+
+ constexpr auto slack_space = 1_MiB;
+ constexpr auto big_size = (kSuperSize - slack_space) / 2;
+ constexpr auto small_size = big_size / 2;
+
+ BlockDeviceInfo super_device("super", kSuperSize, 0, 0, 4_KiB);
+ std::vector<BlockDeviceInfo> devices = {super_device};
+ auto source = MetadataBuilder::New(devices, "super", 1024, 2);
+ auto system = source->AddPartition("system_a", 0);
+ ASSERT_NE(nullptr, system);
+ ASSERT_TRUE(source->ResizePartition(system, big_size));
+ auto vendor = source->AddPartition("vendor_a", 0);
+ ASSERT_NE(nullptr, vendor);
+ ASSERT_TRUE(source->ResizePartition(vendor, big_size));
+ // Create a hole between system and vendor
+ ASSERT_TRUE(source->ResizePartition(system, small_size));
+ auto source_metadata = source->Export();
+ ASSERT_NE(nullptr, source_metadata);
+ ASSERT_TRUE(FlashPartitionTable(opener, fake_super, *source_metadata.get()));
+
+ auto target = MetadataBuilder::NewForUpdate(opener, "super", 0, 1);
+ // Shrink vendor
+ vendor = target->FindPartition("vendor_b");
+ ASSERT_NE(nullptr, vendor);
+ ASSERT_TRUE(target->ResizePartition(vendor, small_size));
+ // Grow system to take hole & saved space from vendor
+ system = target->FindPartition("system_b");
+ ASSERT_NE(nullptr, system);
+ ASSERT_TRUE(target->ResizePartition(system, big_size * 2 - small_size));
+
+ PartitionCowCreator creator{.target_metadata = target.get(),
+ .target_suffix = "_b",
+ .target_partition = system,
+ .current_metadata = source.get(),
+ .current_suffix = "_a"};
+ auto ret = creator.Run();
+ ASSERT_TRUE(ret.has_value());
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index c94fde5..f3994c1 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -58,15 +58,11 @@
using namespace std::chrono_literals;
using namespace std::string_literals;
-// These are not reset between each test because it's expensive to create
-// these resources (starting+connecting to gsid, zero-filling images).
+// Global states. See test_helpers.h.
std::unique_ptr<SnapshotManager> sm;
TestDeviceInfo* test_device = nullptr;
std::string fake_super;
-static constexpr uint64_t kSuperSize = 16_MiB + 4_KiB;
-static constexpr uint64_t kGroupSize = 16_MiB;
-
class SnapshotTest : public ::testing::Test {
public:
SnapshotTest() : dm_(DeviceMapper::Instance()) {}
@@ -743,9 +739,9 @@
}
// Grow all partitions.
- SetSize(sys_, 4_MiB);
- SetSize(vnd_, 4_MiB);
- SetSize(prd_, 4_MiB);
+ SetSize(sys_, 3788_KiB);
+ SetSize(vnd_, 3788_KiB);
+ SetSize(prd_, 3788_KiB);
// Execute the update.
ASSERT_TRUE(sm->BeginUpdate());
@@ -810,6 +806,7 @@
// Test that if new system partitions uses empty space in super, that region is not snapshotted.
TEST_F(SnapshotUpdateTest, DirectWriteEmptySpace) {
+ GTEST_SKIP() << "b/141889746";
SetSize(sys_, 4_MiB);
// vnd_b and prd_b are unchanged.
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
diff --git a/fs_mgr/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/test_helpers.h
index bb19adc..769d21e 100644
--- a/fs_mgr/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/test_helpers.h
@@ -23,6 +23,7 @@
#include <liblp/mock_property_fetcher.h>
#include <liblp/partition_opener.h>
#include <libsnapshot/snapshot.h>
+#include <storage_literals/storage_literals.h>
#include <update_engine/update_metadata.pb.h>
namespace android {
@@ -38,8 +39,17 @@
using testing::NiceMock;
using testing::Return;
+using namespace android::storage_literals;
using namespace std::string_literals;
+// These are not reset between each test because it's expensive to create
+// these resources (starting+connecting to gsid, zero-filling images).
+extern std::unique_ptr<SnapshotManager> sm;
+extern class TestDeviceInfo* test_device;
+extern std::string fake_super;
+static constexpr uint64_t kSuperSize = 16_MiB + 4_KiB;
+static constexpr uint64_t kGroupSize = 16_MiB;
+
// Redirect requests for "super" to our fake super partition.
class TestPartitionOpener final : public android::fs_mgr::PartitionOpener {
public:
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 8a63007..c40c5ef 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -15,7 +15,6 @@
//
liblog_sources = [
- "config_write.cpp",
"log_event_list.cpp",
"log_event_write.cpp",
"logger_lock.cpp",
diff --git a/liblog/README.md b/liblog/README.md
index 98bee9f..871399a 100644
--- a/liblog/README.md
+++ b/liblog/README.md
@@ -96,11 +96,6 @@
int android_log_destroy(android_log_context *ctx)
- #include <log/log_transport.h>
-
- int android_set_log_transport(int transport_flag)
- int android_get_log_transport()
-
Description
-----------
@@ -144,11 +139,6 @@
that was used when opening the sub-log. It is recommended to open the log `ANDROID_LOG_RDONLY` in
these cases.
-`android_set_log_transport()` selects transport filters. Argument is either `LOGGER_DEFAULT`,
-`LOGGER_LOGD`, or `LOGGER_NULL`. Log to logger daemon for default or logd, or drop contents on floor
-respectively. `Both android_set_log_transport()` and `android_get_log_transport()` return the
-current transport mask, or a negative errno for any problems.
-
Errors
------
diff --git a/liblog/config_write.cpp b/liblog/config_write.cpp
deleted file mode 100644
index 6ed893d..0000000
--- a/liblog/config_write.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <log/log_transport.h>
-
-#include "config_write.h"
-#include "logger.h"
-
-struct listnode __android_log_transport_write = {&__android_log_transport_write,
- &__android_log_transport_write};
-struct listnode __android_log_persist_write = {&__android_log_persist_write,
- &__android_log_persist_write};
-
-static void __android_log_add_transport(struct listnode* list,
- struct android_log_transport_write* transport) {
- uint32_t i;
-
- /* Try to keep one functioning transport for each log buffer id */
- for (i = LOG_ID_MIN; i < LOG_ID_MAX; i++) {
- struct android_log_transport_write* transp;
-
- if (list_empty(list)) {
- if (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0)) {
- list_add_tail(list, &transport->node);
- return;
- }
- } else {
- write_transport_for_each(transp, list) {
- if (!transp->available) {
- return;
- }
- if (((*transp->available)(static_cast<log_id_t>(i)) < 0) &&
- (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0))) {
- list_add_tail(list, &transport->node);
- return;
- }
- }
- }
- }
-}
-
-void __android_log_config_write() {
- if ((__android_log_transport == LOGGER_DEFAULT) || (__android_log_transport & LOGGER_LOGD)) {
-#if (FAKE_LOG_DEVICE == 0)
- extern struct android_log_transport_write logdLoggerWrite;
- extern struct android_log_transport_write pmsgLoggerWrite;
-
- __android_log_add_transport(&__android_log_transport_write, &logdLoggerWrite);
- __android_log_add_transport(&__android_log_persist_write, &pmsgLoggerWrite);
-#else
- extern struct android_log_transport_write fakeLoggerWrite;
-
- __android_log_add_transport(&__android_log_transport_write, &fakeLoggerWrite);
-#endif
- }
-}
-
-void __android_log_config_write_close() {
- struct android_log_transport_write* transport;
- struct listnode* n;
-
- write_transport_for_each_safe(transport, n, &__android_log_transport_write) {
- transport->logMask = 0;
- list_remove(&transport->node);
- }
- write_transport_for_each_safe(transport, n, &__android_log_persist_write) {
- transport->logMask = 0;
- list_remove(&transport->node);
- }
-}
diff --git a/liblog/config_write.h b/liblog/config_write.h
deleted file mode 100644
index a901f13..0000000
--- a/liblog/config_write.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cutils/list.h>
-
-#include "log_portability.h"
-
-__BEGIN_DECLS
-
-extern struct listnode __android_log_transport_write;
-extern struct listnode __android_log_persist_write;
-
-#define write_transport_for_each(transp, transports) \
- for ((transp) = node_to_item((transports)->next, \
- struct android_log_transport_write, node); \
- ((transp) != node_to_item((transports), \
- struct android_log_transport_write, node)) && \
- ((transp) != node_to_item((transp)->node.next, \
- struct android_log_transport_write, node)); \
- (transp) = node_to_item((transp)->node.next, \
- struct android_log_transport_write, node))
-
-#define write_transport_for_each_safe(transp, n, transports) \
- for ((transp) = node_to_item((transports)->next, \
- struct android_log_transport_write, node), \
- (n) = (transp)->node.next; \
- ((transp) != node_to_item((transports), \
- struct android_log_transport_write, node)) && \
- ((transp) != \
- node_to_item((n), struct android_log_transport_write, node)); \
- (transp) = node_to_item((n), struct android_log_transport_write, node), \
- (n) = (transp)->node.next)
-
-void __android_log_config_write();
-void __android_log_config_write_close();
-
-__END_DECLS
diff --git a/liblog/fake_writer.cpp b/liblog/fake_writer.cpp
index c0b0e69..4d07caa 100644
--- a/liblog/fake_writer.cpp
+++ b/liblog/fake_writer.cpp
@@ -20,7 +20,6 @@
#include <log/log.h>
-#include "config_write.h"
#include "fake_log_device.h"
#include "log_portability.h"
#include "logger.h"
@@ -32,9 +31,9 @@
static int logFds[(int)LOG_ID_MAX] = {-1, -1, -1, -1, -1, -1};
struct android_log_transport_write fakeLoggerWrite = {
- .node = {&fakeLoggerWrite.node, &fakeLoggerWrite.node},
- .context.priv = &logFds,
.name = "fake",
+ .logMask = 0,
+ .context.priv = &logFds,
.available = NULL,
.open = fakeOpen,
.close = fakeClose,
diff --git a/liblog/include/log/log_transport.h b/liblog/include/log/log_transport.h
deleted file mode 100644
index bda7c25..0000000
--- a/liblog/include/log/log_transport.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
-**
-** Copyright 2017, The Android Open Source Project
-**
-** This file is dual licensed. It may be redistributed and/or modified
-** under the terms of the Apache 2.0 License OR version 2 of the GNU
-** General Public License.
-*/
-
-#pragma once
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Logging transports, bit mask to select features. Function returns selection.
- */
-/* clang-format off */
-#define LOGGER_DEFAULT 0x00
-#define LOGGER_LOGD 0x01
-#define LOGGER_KERNEL 0x02 /* Reserved/Deprecated */
-#define LOGGER_NULL 0x04 /* Does not release resources of other selections */
-#define LOGGER_RESERVED 0x08 /* Reserved, previously for logging to local memory */
-#define LOGGER_RESERVED2 0x10 /* Reserved, previously for logs sent to stderr */
-/* clang-format on */
-
-/* Both return the selected transport flag mask, or negative errno */
-int android_set_log_transport(int transport_flag);
-int android_get_log_transport();
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index d5bf844..e372dce 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -66,7 +66,6 @@
struct android_log_transport_context* transp, char* buf, size_t len);
struct android_log_transport_read logdLoggerRead = {
- .node = {&logdLoggerRead.node, &logdLoggerRead.node},
.name = "logd",
.available = logdAvailable,
.version = logdVersion,
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
index 2c64b0b..09aaffb 100644
--- a/liblog/logd_writer.cpp
+++ b/liblog/logd_writer.cpp
@@ -34,7 +34,6 @@
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
-#include "config_write.h"
#include "log_portability.h"
#include "logger.h"
#include "uio.h"
@@ -48,9 +47,9 @@
static int logdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
struct android_log_transport_write logdLoggerWrite = {
- .node = {&logdLoggerWrite.node, &logdLoggerWrite.node},
- .context.sock = -EBADF,
.name = "logd",
+ .logMask = 0,
+ .context.sock = -EBADF,
.available = logdAvailable,
.open = logdOpen,
.close = logdClose,
diff --git a/liblog/logger.h b/liblog/logger.h
index 4b4ef5f..8cae66c 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -31,12 +31,9 @@
void* priv;
atomic_int sock;
atomic_int fd;
- struct listnode* node;
- atomic_uintptr_t atomic_pointer;
};
struct android_log_transport_write {
- struct listnode node;
const char* name; /* human name to describe the transport */
unsigned logMask; /* mask cache of available() success */
union android_log_context_union context; /* Initialized by static allocation */
@@ -54,7 +51,6 @@
struct android_log_logger;
struct android_log_transport_read {
- struct listnode node;
const char* name; /* human name to describe the transport */
/* Does not cause resources to be taken */
@@ -149,6 +145,4 @@
int __android_log_trylock();
void __android_log_unlock();
-extern int __android_log_transport;
-
__END_DECLS
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index 7fc8747..abcead7 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -25,17 +25,18 @@
#endif
#include <log/event_tag_map.h>
-#include <log/log_transport.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
-#include "config_write.h"
#include "log_portability.h"
#include "logger.h"
#include "uio.h"
#define LOG_BUF_SIZE 1024
+android_log_transport_write* android_log_write = nullptr;
+android_log_transport_write* android_log_persist_write = nullptr;
+
static int __write_to_log_init(log_id_t, struct iovec* vec, size_t nr);
static int (*write_to_log)(log_id_t, struct iovec* vec, size_t nr) = __write_to_log_init;
@@ -105,7 +106,6 @@
* Release any logger resources. A new log write will immediately re-acquire.
*/
void __android_log_close() {
- struct android_log_transport_write* transport;
#if defined(__ANDROID__)
EventTagMap* m;
#endif
@@ -124,19 +124,16 @@
* disengenuous use of this function.
*/
- write_transport_for_each(transport, &__android_log_persist_write) {
- if (transport->close) {
- (*transport->close)();
- }
+ if (android_log_write != nullptr) {
+ android_log_write->close();
}
- write_transport_for_each(transport, &__android_log_transport_write) {
- if (transport->close) {
- (*transport->close)();
- }
+ if (android_log_persist_write != nullptr) {
+ android_log_persist_write->close();
}
- __android_log_config_write_close();
+ android_log_write = nullptr;
+ android_log_persist_write = nullptr;
#if defined(__ANDROID__)
/*
@@ -161,52 +158,52 @@
#endif
}
+static bool transport_initialize(android_log_transport_write* transport) {
+ if (transport == nullptr) {
+ return false;
+ }
+
+ __android_log_cache_available(transport);
+ if (!transport->logMask) {
+ return false;
+ }
+
+ // TODO: Do we actually need to call close() if open() fails?
+ if (transport->open() < 0) {
+ transport->close();
+ return false;
+ }
+
+ return true;
+}
+
/* log_init_lock assumed */
static int __write_to_log_initialize() {
- struct android_log_transport_write* transport;
- struct listnode* n;
- int i = 0, ret = 0;
+#if (FAKE_LOG_DEVICE == 0)
+ extern struct android_log_transport_write logdLoggerWrite;
+ extern struct android_log_transport_write pmsgLoggerWrite;
- __android_log_config_write();
- write_transport_for_each_safe(transport, n, &__android_log_transport_write) {
- __android_log_cache_available(transport);
- if (!transport->logMask) {
- list_remove(&transport->node);
- continue;
- }
- if (!transport->open || ((*transport->open)() < 0)) {
- if (transport->close) {
- (*transport->close)();
- }
- list_remove(&transport->node);
- continue;
- }
- ++ret;
- }
- write_transport_for_each_safe(transport, n, &__android_log_persist_write) {
- __android_log_cache_available(transport);
- if (!transport->logMask) {
- list_remove(&transport->node);
- continue;
- }
- if (!transport->open || ((*transport->open)() < 0)) {
- if (transport->close) {
- (*transport->close)();
- }
- list_remove(&transport->node);
- continue;
- }
- ++i;
- }
- if (!ret && !i) {
+ android_log_write = &logdLoggerWrite;
+ android_log_persist_write = &pmsgLoggerWrite;
+#else
+ extern struct android_log_transport_write fakeLoggerWrite;
+
+ android_log_write = &fakeLoggerWrite;
+#endif
+
+ if (!transport_initialize(android_log_write)) {
+ android_log_write = nullptr;
return -ENODEV;
}
- return ret;
+ if (!transport_initialize(android_log_persist_write)) {
+ android_log_persist_write = nullptr;
+ }
+
+ return 1;
}
static int __write_to_log_daemon(log_id_t log_id, struct iovec* vec, size_t nr) {
- struct android_log_transport_write* node;
int ret, save_errno;
struct timespec ts;
size_t len, i;
@@ -283,28 +280,11 @@
return -EPERM;
}
} else {
- /* Validate the incoming tag, tag content can not split across iovec */
- char prio = ANDROID_LOG_VERBOSE;
- const char* tag = static_cast<const char*>(vec[0].iov_base);
- size_t len = vec[0].iov_len;
- if (!tag) {
- len = 0;
- }
- if (len > 0) {
- prio = *tag;
- if (len > 1) {
- --len;
- ++tag;
- } else {
- len = vec[1].iov_len;
- tag = ((const char*)vec[1].iov_base);
- if (!tag) {
- len = 0;
- }
- }
- }
+ int prio = *static_cast<int*>(vec[0].iov_base);
+ const char* tag = static_cast<const char*>(vec[1].iov_base);
+ size_t len = vec[1].iov_len;
/* tag must be nul terminated */
- if (tag && strnlen(tag, len) >= len) {
+ if (strnlen(tag, len) >= len) {
tag = NULL;
}
@@ -325,20 +305,17 @@
ret = 0;
i = 1 << log_id;
- write_transport_for_each(node, &__android_log_transport_write) {
- if (node->logMask & i) {
- ssize_t retval;
- retval = (*node->write)(log_id, &ts, vec, nr);
- if (ret >= 0) {
- ret = retval;
- }
+
+ if (android_log_write != nullptr && (android_log_write->logMask & i)) {
+ ssize_t retval;
+ retval = android_log_write->write(log_id, &ts, vec, nr);
+ if (ret >= 0) {
+ ret = retval;
}
}
- write_transport_for_each(node, &__android_log_persist_write) {
- if (node->logMask & i) {
- (void)(*node->write)(log_id, &ts, vec, nr);
- }
+ if (android_log_persist_write != nullptr && (android_log_persist_write->logMask & i)) {
+ android_log_persist_write->write(log_id, &ts, vec, nr);
}
errno = save_errno;
@@ -354,9 +331,6 @@
ret = __write_to_log_initialize();
if (ret < 0) {
__android_log_unlock();
- if (!list_empty(&__android_log_persist_write)) {
- __write_to_log_daemon(log_id, vec, nr);
- }
errno = save_errno;
return ret;
}
@@ -546,81 +520,3 @@
return write_to_log(LOG_ID_SECURITY, vec, 4);
}
-
-static int __write_to_log_null(log_id_t log_id, struct iovec* vec, size_t nr) {
- size_t len, i;
-
- if ((log_id < LOG_ID_MIN) || (log_id >= LOG_ID_MAX)) {
- return -EINVAL;
- }
-
- for (len = i = 0; i < nr; ++i) {
- len += vec[i].iov_len;
- }
- if (!len) {
- return -EINVAL;
- }
- return len;
-}
-
-/* Following functions need access to our internal write_to_log status */
-
-int __android_log_transport;
-
-int android_set_log_transport(int transport_flag) {
- int retval;
-
- if (transport_flag < 0) {
- return -EINVAL;
- }
-
- retval = LOGGER_NULL;
-
- __android_log_lock();
-
- if (transport_flag & LOGGER_NULL) {
- write_to_log = __write_to_log_null;
-
- __android_log_unlock();
-
- return retval;
- }
-
- __android_log_transport &= LOGGER_LOGD;
-
- transport_flag &= LOGGER_LOGD;
-
- if (__android_log_transport != transport_flag) {
- __android_log_transport = transport_flag;
- __android_log_config_write_close();
-
- write_to_log = __write_to_log_init;
- /* generically we only expect these two values for write_to_log */
- } else if ((write_to_log != __write_to_log_init) && (write_to_log != __write_to_log_daemon)) {
- write_to_log = __write_to_log_init;
- }
-
- retval = __android_log_transport;
-
- __android_log_unlock();
-
- return retval;
-}
-
-int android_get_log_transport() {
- int ret = LOGGER_DEFAULT;
-
- __android_log_lock();
- if (write_to_log == __write_to_log_null) {
- ret = LOGGER_NULL;
- } else {
- __android_log_transport &= LOGGER_LOGD;
- ret = __android_log_transport;
- if ((write_to_log != __write_to_log_init) && (write_to_log != __write_to_log_daemon)) {
- ret = -EINVAL;
- }
- }
- __android_log_unlock();
-
- return ret;
-}
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
index 005fec8..2db45a1 100644
--- a/liblog/pmsg_reader.cpp
+++ b/liblog/pmsg_reader.cpp
@@ -37,7 +37,6 @@
struct android_log_transport_context* transp);
struct android_log_transport_read pmsgLoggerRead = {
- .node = {&pmsgLoggerRead.node, &pmsgLoggerRead.node},
.name = "pmsg",
.available = pmsgAvailable,
.version = pmsgVersion,
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
index 202b0fe..54980d9 100644
--- a/liblog/pmsg_writer.cpp
+++ b/liblog/pmsg_writer.cpp
@@ -29,7 +29,6 @@
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
-#include "config_write.h"
#include "log_portability.h"
#include "logger.h"
#include "uio.h"
@@ -40,9 +39,9 @@
static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
struct android_log_transport_write pmsgLoggerWrite = {
- .node = {&pmsgLoggerWrite.node, &pmsgLoggerWrite.node},
- .context.fd = -1,
.name = "pmsg",
+ .logMask = 0,
+ .context.fd = -1,
.available = pmsgAvailable,
.open = pmsgOpen,
.close = pmsgClose,
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 7163743..4642b9b 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -29,7 +29,6 @@
#include <benchmark/benchmark.h>
#include <cutils/sockets.h>
#include <log/event_tag_map.h>
-#include <log/log_transport.h>
#include <private/android_logger.h>
BENCHMARK_MAIN();
@@ -73,21 +72,6 @@
}
BENCHMARK(BM_log_maximum);
-static void set_log_null() {
- android_set_log_transport(LOGGER_NULL);
-}
-
-static void set_log_default() {
- android_set_log_transport(LOGGER_DEFAULT);
-}
-
-static void BM_log_maximum_null(benchmark::State& state) {
- set_log_null();
- BM_log_maximum(state);
- set_log_default();
-}
-BENCHMARK(BM_log_maximum_null);
-
/*
* Measure the time it takes to collect the time using
* discrete acquisition (state.PauseTiming() to state.ResumeTiming())
@@ -618,13 +602,6 @@
}
BENCHMARK(BM_log_event_overhead_42);
-static void BM_log_event_overhead_null(benchmark::State& state) {
- set_log_null();
- BM_log_event_overhead(state);
- set_log_default();
-}
-BENCHMARK(BM_log_event_overhead_null);
-
/*
* Measure the time it takes to submit the android event logging call
* using discrete acquisition under very-light load (<1% CPU utilization).
@@ -639,15 +616,6 @@
}
BENCHMARK(BM_log_light_overhead);
-static void BM_log_light_overhead_null(benchmark::State& state) {
- set_log_null();
- BM_log_light_overhead(state);
- set_log_default();
-}
-// Default gets out of hand for this test, so we set a reasonable number of
-// iterations for a timely result.
-BENCHMARK(BM_log_light_overhead_null)->Iterations(500);
-
static void caught_latency(int /*signum*/) {
unsigned long long v = 0xDEADBEEFA55A5AA5ULL;
diff --git a/liblog/tests/log_wrap_test.cpp b/liblog/tests/log_wrap_test.cpp
index ebf0b15..c7dd8e8 100644
--- a/liblog/tests/log_wrap_test.cpp
+++ b/liblog/tests/log_wrap_test.cpp
@@ -27,12 +27,9 @@
#include <log/log_properties.h>
#include <log/log_read.h>
#include <log/log_time.h>
-#include <log/log_transport.h>
#ifdef __ANDROID__
static void read_with_wrap() {
- android_set_log_transport(LOGGER_LOGD);
-
// Read the last line in the log to get a starting timestamp. We're assuming
// the log is not empty.
const int mode = ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
diff --git a/libnativeloader/README.md b/libnativeloader/README.md
index 46f6fdd..57b9001 100644
--- a/libnativeloader/README.md
+++ b/libnativeloader/README.md
@@ -43,7 +43,7 @@
- `/vendor/etc/public.libraries.txt`: libraries in `/vendor/lib` that are
specific to the underlying SoC, e.g. GPU, DSP, etc.
- `/{system|product}/etc/public.libraries-<companyname>.txt`: libraries in
-`/{system|system}/lib` that a device manufacturer has newly added. The
+`/{system|product}/lib` that a device manufacturer has newly added. The
libraries should be named as `lib<name>.<companyname>.so` as in
`libFoo.acme.so`.
@@ -73,8 +73,8 @@
linker namespaces and finding an already created linker namespace for a given
classloader.
-`native_loader_namesapces.cpp` implements the class `NativeLoaderNamespace` that
-models a linker namespace. It's main job is to abstract the two types of the
+`native_loader_namespace.cpp` implements the class `NativeLoaderNamespace` that
+models a linker namespace. Its main job is to abstract the two types of the
dynamic linker interface so that other parts of this library do not have to know
the differences of the interfaces.
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 7c191be..7b6dde2 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -117,43 +117,11 @@
bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
bool use_fd_cache) {
- const TaskProfiles& tp = TaskProfiles::GetInstance();
-
- for (const auto& name : profiles) {
- TaskProfile* profile = tp.GetProfile(name);
- if (profile != nullptr) {
- if (use_fd_cache) {
- profile->EnableResourceCaching();
- }
- if (!profile->ExecuteForProcess(uid, pid)) {
- PLOG(WARNING) << "Failed to apply " << name << " process profile";
- }
- } else {
- PLOG(WARNING) << "Failed to find " << name << "process profile";
- }
- }
-
- return true;
+ return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, use_fd_cache);
}
bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache) {
- const TaskProfiles& tp = TaskProfiles::GetInstance();
-
- for (const auto& name : profiles) {
- TaskProfile* profile = tp.GetProfile(name);
- if (profile != nullptr) {
- if (use_fd_cache) {
- profile->EnableResourceCaching();
- }
- if (!profile->ExecuteForTask(tid)) {
- PLOG(WARNING) << "Failed to apply " << name << " task profile";
- }
- } else {
- PLOG(WARNING) << "Failed to find " << name << "task profile";
- }
- }
-
- return true;
+ return TaskProfiles::GetInstance().SetTaskProfiles(tid, profiles, use_fd_cache);
}
static std::string ConvertUidToPath(const char* cgroup, uid_t uid) {
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index 74a39cd..608f007 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -15,7 +15,6 @@
"Controller": "cpuset",
"File": "top-app/cpus"
},
-
{
"Name": "MemLimit",
"Controller": "memory",
@@ -494,5 +493,52 @@
}
]
}
+ ],
+
+ "AggregateProfiles": [
+ {
+ "Name": "SCHED_SP_DEFAULT",
+ "Profiles": [ "TimerSlackNormal" ]
+ },
+ {
+ "Name": "SCHED_SP_BACKGROUND",
+ "Profiles": [ "HighEnergySaving", "LowIoPriority", "TimerSlackHigh" ]
+ },
+ {
+ "Name": "SCHED_SP_FOREGROUND",
+ "Profiles": [ "HighPerformance", "HighIoPriority", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "SCHED_SP_TOP_APP",
+ "Profiles": [ "MaxPerformance", "MaxIoPriority", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "SCHED_SP_RT_APP",
+ "Profiles": [ "RealtimePerformance", "MaxIoPriority", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "CPUSET_SP_DEFAULT",
+ "Profiles": [ "TimerSlackNormal" ]
+ },
+ {
+ "Name": "CPUSET_SP_BACKGROUND",
+ "Profiles": [ "HighEnergySaving", "ProcessCapacityLow", "LowIoPriority", "TimerSlackHigh" ]
+ },
+ {
+ "Name": "CPUSET_SP_FOREGROUND",
+ "Profiles": [ "HighPerformance", "ProcessCapacityHigh", "HighIoPriority", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "CPUSET_SP_TOP_APP",
+ "Profiles": [ "MaxPerformance", "ProcessCapacityMax", "MaxIoPriority", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "CPUSET_SP_SYSTEM",
+ "Profiles": [ "ServiceCapacityLow", "TimerSlackNormal" ]
+ },
+ {
+ "Name": "CPUSET_SP_RESTRICTED",
+ "Profiles": [ "ServiceCapacityRestricted", "TimerSlackNormal" ]
+ }
]
}
diff --git a/libprocessgroup/profiles/task_profiles.proto b/libprocessgroup/profiles/task_profiles.proto
index 578f0d3..1de4395 100644
--- a/libprocessgroup/profiles/task_profiles.proto
+++ b/libprocessgroup/profiles/task_profiles.proto
@@ -18,10 +18,11 @@
package android.profiles;
-// Next: 3
+// Next: 4
message TaskProfiles {
repeated Attribute attributes = 1 [json_name = "Attributes"];
repeated Profile profiles = 2 [json_name = "Profiles"];
+ repeated AggregateProfiles aggregateprofiles = 3 [json_name = "AggregateProfiles"];
}
// Next: 4
@@ -42,3 +43,9 @@
string name = 1 [json_name = "Name"];
map<string, string> params = 2 [json_name = "Params"];
}
+
+// Next: 3
+message AggregateProfiles {
+ string name = 1 [json_name = "Name"];
+ repeated string profiles = 2 [json_name = "Profiles"];
+}
diff --git a/libprocessgroup/sched_policy.cpp b/libprocessgroup/sched_policy.cpp
index c83df1a..6b0ab87 100644
--- a/libprocessgroup/sched_policy.cpp
+++ b/libprocessgroup/sched_policy.cpp
@@ -46,34 +46,17 @@
switch (policy) {
case SP_BACKGROUND:
- return SetTaskProfiles(tid,
- {"HighEnergySaving", "ProcessCapacityLow", "LowIoPriority",
- "TimerSlackHigh"},
- true)
- ? 0
- : -1;
+ return SetTaskProfiles(tid, {"CPUSET_SP_BACKGROUND"}, true) ? 0 : -1;
case SP_FOREGROUND:
case SP_AUDIO_APP:
case SP_AUDIO_SYS:
- return SetTaskProfiles(tid,
- {"HighPerformance", "ProcessCapacityHigh", "HighIoPriority",
- "TimerSlackNormal"},
- true)
- ? 0
- : -1;
+ return SetTaskProfiles(tid, {"CPUSET_SP_FOREGROUND"}, true) ? 0 : -1;
case SP_TOP_APP:
- return SetTaskProfiles(tid,
- {"MaxPerformance", "ProcessCapacityMax", "MaxIoPriority",
- "TimerSlackNormal"},
- true)
- ? 0
- : -1;
+ return SetTaskProfiles(tid, {"CPUSET_SP_TOP_APP"}, true) ? 0 : -1;
case SP_SYSTEM:
- return SetTaskProfiles(tid, {"ServiceCapacityLow", "TimerSlackNormal"}, true) ? 0 : -1;
+ return SetTaskProfiles(tid, {"CPUSET_SP_SYSTEM"}, true) ? 0 : -1;
case SP_RESTRICTED:
- return SetTaskProfiles(tid, {"ServiceCapacityRestricted", "TimerSlackNormal"}, true)
- ? 0
- : -1;
+ return SetTaskProfiles(tid, {"CPUSET_SP_RESTRICTED"}, true) ? 0 : -1;
default:
break;
}
@@ -134,29 +117,17 @@
switch (policy) {
case SP_BACKGROUND:
- return SetTaskProfiles(tid, {"HighEnergySaving", "LowIoPriority", "TimerSlackHigh"},
- true)
- ? 0
- : -1;
+ return SetTaskProfiles(tid, {"SCHED_SP_BACKGROUND"}, true) ? 0 : -1;
case SP_FOREGROUND:
case SP_AUDIO_APP:
case SP_AUDIO_SYS:
- return SetTaskProfiles(tid, {"HighPerformance", "HighIoPriority", "TimerSlackNormal"},
- true)
- ? 0
- : -1;
+ return SetTaskProfiles(tid, {"SCHED_SP_FOREGROUND"}, true) ? 0 : -1;
case SP_TOP_APP:
- return SetTaskProfiles(tid, {"MaxPerformance", "MaxIoPriority", "TimerSlackNormal"},
- true)
- ? 0
- : -1;
+ return SetTaskProfiles(tid, {"SCHED_SP_TOP_APP"}, true) ? 0 : -1;
case SP_RT_APP:
- return SetTaskProfiles(
- tid, {"RealtimePerformance", "MaxIoPriority", "TimerSlackNormal"}, true)
- ? 0
- : -1;
+ return SetTaskProfiles(tid, {"SCHED_SP_RT_APP"}, true) ? 0 : -1;
default:
- return SetTaskProfiles(tid, {"TimerSlackNormal"}, true) ? 0 : -1;
+ return SetTaskProfiles(tid, {"SCHED_SP_DEFAULT"}, true) ? 0 : -1;
}
return 0;
diff --git a/libprocessgroup/task_profiles.cpp b/libprocessgroup/task_profiles.cpp
index aee5f0c..9447f86 100644
--- a/libprocessgroup/task_profiles.cpp
+++ b/libprocessgroup/task_profiles.cpp
@@ -268,6 +268,26 @@
return true;
}
+bool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {
+ for (const auto& profile : profiles_) {
+ profile->EnableResourceCaching();
+ if (!profile->ExecuteForProcess(uid, pid)) {
+ PLOG(WARNING) << "ExecuteForProcess failed for aggregate profile";
+ }
+ }
+ return true;
+}
+
+bool ApplyProfileAction::ExecuteForTask(int tid) const {
+ for (const auto& profile : profiles_) {
+ profile->EnableResourceCaching();
+ if (!profile->ExecuteForTask(tid)) {
+ PLOG(WARNING) << "ExecuteForTask failed for aggregate profile";
+ }
+ }
+ return true;
+}
+
bool TaskProfile::ExecuteForProcess(uid_t uid, pid_t pid) const {
for (const auto& element : elements_) {
if (!element->ExecuteForProcess(uid, pid)) {
@@ -373,15 +393,13 @@
}
}
- std::map<std::string, std::string> params;
-
const Json::Value& profiles_val = root["Profiles"];
for (Json::Value::ArrayIndex i = 0; i < profiles_val.size(); ++i) {
const Json::Value& profile_val = profiles_val[i];
std::string profile_name = profile_val["Name"].asString();
const Json::Value& actions = profile_val["Actions"];
- auto profile = std::make_unique<TaskProfile>();
+ auto profile = std::make_shared<TaskProfile>();
for (Json::Value::ArrayIndex act_idx = 0; act_idx < actions.size(); ++act_idx) {
const Json::Value& action_val = actions[act_idx];
@@ -440,7 +458,38 @@
LOG(WARNING) << "Unknown profile action: " << action_name;
}
}
- profiles_[profile_name] = std::move(profile);
+ profiles_[profile_name] = profile;
+ }
+
+ const Json::Value& aggregateprofiles_val = root["AggregateProfiles"];
+ for (Json::Value::ArrayIndex i = 0; i < aggregateprofiles_val.size(); ++i) {
+ const Json::Value& aggregateprofile_val = aggregateprofiles_val[i];
+
+ std::string aggregateprofile_name = aggregateprofile_val["Name"].asString();
+ const Json::Value& aggregateprofiles = aggregateprofile_val["Profiles"];
+ std::vector<std::shared_ptr<TaskProfile>> profiles;
+ bool ret = true;
+
+ for (Json::Value::ArrayIndex pf_idx = 0; pf_idx < aggregateprofiles.size(); ++pf_idx) {
+ std::string profile_name = aggregateprofiles[pf_idx].asString();
+
+ if (profile_name == aggregateprofile_name) {
+ LOG(WARNING) << "AggregateProfiles: recursive profile name: " << profile_name;
+ ret = false;
+ break;
+ } else if (profiles_.find(profile_name) == profiles_.end()) {
+ LOG(WARNING) << "AggregateProfiles: undefined profile name: " << profile_name;
+ ret = false;
+ break;
+ } else {
+ profiles.push_back(profiles_[profile_name]);
+ }
+ }
+ if (ret) {
+ auto profile = std::make_shared<TaskProfile>();
+ profile->Add(std::make_unique<ApplyProfileAction>(profiles));
+ profiles_[aggregateprofile_name] = profile;
+ }
}
return true;
@@ -463,3 +512,39 @@
}
return nullptr;
}
+
+bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,
+ const std::vector<std::string>& profiles, bool use_fd_cache) {
+ for (const auto& name : profiles) {
+ TaskProfile* profile = GetProfile(name);
+ if (profile != nullptr) {
+ if (use_fd_cache) {
+ profile->EnableResourceCaching();
+ }
+ if (!profile->ExecuteForProcess(uid, pid)) {
+ PLOG(WARNING) << "Failed to apply " << name << " process profile";
+ }
+ } else {
+ PLOG(WARNING) << "Failed to find " << name << "process profile";
+ }
+ }
+ return true;
+}
+
+bool TaskProfiles::SetTaskProfiles(int tid, const std::vector<std::string>& profiles,
+ bool use_fd_cache) {
+ for (const auto& name : profiles) {
+ TaskProfile* profile = GetProfile(name);
+ if (profile != nullptr) {
+ if (use_fd_cache) {
+ profile->EnableResourceCaching();
+ }
+ if (!profile->ExecuteForTask(tid)) {
+ PLOG(WARNING) << "Failed to apply " << name << " task profile";
+ }
+ } else {
+ PLOG(WARNING) << "Failed to find " << name << "task profile";
+ }
+ }
+ return true;
+}
diff --git a/libprocessgroup/task_profiles.h b/libprocessgroup/task_profiles.h
index 891d5b5..9f2308c 100644
--- a/libprocessgroup/task_profiles.h
+++ b/libprocessgroup/task_profiles.h
@@ -154,6 +154,19 @@
std::vector<std::unique_ptr<ProfileAction>> elements_;
};
+// Set aggregate profile element
+class ApplyProfileAction : public ProfileAction {
+ public:
+ ApplyProfileAction(const std::vector<std::shared_ptr<TaskProfile>>& profiles)
+ : profiles_(profiles) {}
+
+ virtual bool ExecuteForProcess(uid_t uid, pid_t pid) const;
+ virtual bool ExecuteForTask(int tid) const;
+
+ private:
+ std::vector<std::shared_ptr<TaskProfile>> profiles_;
+};
+
class TaskProfiles {
public:
// Should be used by all users
@@ -162,9 +175,12 @@
TaskProfile* GetProfile(const std::string& name) const;
const ProfileAttribute* GetAttribute(const std::string& name) const;
void DropResourceCaching() const;
+ bool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles,
+ bool use_fd_cache);
+ bool SetTaskProfiles(int tid, const std::vector<std::string>& profiles, bool use_fd_cache);
private:
- std::map<std::string, std::unique_ptr<TaskProfile>> profiles_;
+ std::map<std::string, std::shared_ptr<TaskProfile>> profiles_;
std::map<std::string, std::unique_ptr<ProfileAttribute>> attributes_;
TaskProfiles();
diff --git a/libutils/String16.cpp b/libutils/String16.cpp
index 5c3cf32..e2a8c59 100644
--- a/libutils/String16.cpp
+++ b/libutils/String16.cpp
@@ -346,7 +346,6 @@
if (isStaticString()) {
buf = static_cast<SharedBuffer*>(alloc((size() + 1) * sizeof(char16_t)));
if (buf) {
- buf->acquire();
memcpy(buf->data(), mString, (size() + 1) * sizeof(char16_t));
}
} else {
@@ -365,7 +364,6 @@
}
buf = static_cast<SharedBuffer*>(alloc(newSize));
if (buf) {
- buf->acquire();
memcpy(buf->data(), mString, copySize);
}
} else {
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
index 4c5ca03..e8e125b 100644
--- a/lmkd/Android.bp
+++ b/lmkd/Android.bp
@@ -1,3 +1,15 @@
+cc_defaults {
+ name: "stats_defaults",
+
+ product_variables: {
+ use_lmkd_stats_log: {
+ cflags: [
+ "-DLMKD_LOG_STATS"
+ ],
+ },
+ },
+}
+
cc_binary {
name: "lmkd",
@@ -15,13 +27,7 @@
local_include_dirs: ["include"],
cflags: ["-Werror", "-DLMKD_TRACE_KILLS"],
init_rc: ["lmkd.rc"],
- product_variables: {
- use_lmkd_stats_log: {
- cflags: [
- "-DLMKD_LOG_STATS"
- ],
- },
- },
+ defaults: ["stats_defaults"],
logtags: ["event.logtags"],
}
@@ -32,6 +38,7 @@
"-Wall",
"-Werror",
],
+ defaults: ["stats_defaults"],
shared_libs: [
"liblog",
],
diff --git a/lmkd/README.md b/lmkd/README.md
index 656a6ea..8a73692 100644
--- a/lmkd/README.md
+++ b/lmkd/README.md
@@ -60,6 +60,31 @@
any eligible task (fast decision). Default = false
ro.lmk.kill_timeout_ms: duration in ms after a kill when no additional
- kill will be done, Default = 0 (disabled)
+ kill will be done. Default = 0 (disabled)
ro.lmk.debug: enable lmkd debug logs, Default = false
+
+ ro.lmk.swap_free_low_percentage: level of free swap as a percentage of the
+ total swap space used as a threshold to consider
+ the system as swap space starved. Default for
+ low-RAM devices = 10, for high-end devices = 20
+
+ ro.lmk.thrashing_limit: number of workingset refaults as a percentage of
+ the file-backed pagecache size used as a threshold
+ to consider system thrashing its pagecache.
+ Default for low-RAM devices = 30, for high-end
+ devices = 100
+
+ ro.lmk.thrashing_limit_decay: thrashing threshold decay expressed as a
+ percentage of the original threshold used to lower
+ the threshold when system does not recover even
+ after a kill. Default for low-RAM devices = 50,
+ for high-end devices = 10
+
+ ro.lmk.psi_partial_stall_ms: partial PSI stall threshold in milliseconds for
+ triggering low memory notification. Default for
+ low-RAM devices = 200, for high-end devices = 70
+
+ ro.lmk.psi_complete_stall_ms: complete PSI stall threshold in milliseconds for
+ triggering critical memory notification. Default =
+ 700
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index bf0ae4d..449088a 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -47,9 +47,7 @@
#include <psi/psi.h>
#include <system/thread_defs.h>
-#ifdef LMKD_LOG_STATS
#include "statslog.h"
-#endif
/*
* Define LMKD_TRACE_KILLS to record lmkd kills in kernel traces
@@ -79,9 +77,12 @@
#define MEMCG_MEMORYSW_USAGE "/dev/memcg/memory.memsw.usage_in_bytes"
#define ZONEINFO_PATH "/proc/zoneinfo"
#define MEMINFO_PATH "/proc/meminfo"
+#define VMSTAT_PATH "/proc/vmstat"
#define PROC_STATUS_TGID_FIELD "Tgid:"
#define LINE_MAX 128
+#define PERCEPTIBLE_APP_ADJ 200
+
/* Android Logger event logtags (see event.logtags) */
#define MEMINFO_LOG_TAG 10195355
@@ -110,15 +111,34 @@
* PSI_WINDOW_SIZE_MS after the event happens.
*/
#define PSI_WINDOW_SIZE_MS 1000
-/* Polling period after initial PSI signal */
-#define PSI_POLL_PERIOD_MS 10
-/* Poll for the duration of one window after initial PSI signal */
-#define PSI_POLL_COUNT (PSI_WINDOW_SIZE_MS / PSI_POLL_PERIOD_MS)
+/* Polling period after PSI signal when pressure is high */
+#define PSI_POLL_PERIOD_SHORT_MS 10
+/* Polling period after PSI signal when pressure is low */
+#define PSI_POLL_PERIOD_LONG_MS 100
#define min(a, b) (((a) < (b)) ? (a) : (b))
+#define max(a, b) (((a) > (b)) ? (a) : (b))
#define FAIL_REPORT_RLIMIT_MS 1000
+/*
+ * System property defaults
+ */
+/* ro.lmk.swap_free_low_percentage property defaults */
+#define DEF_LOW_SWAP_LOWRAM 10
+#define DEF_LOW_SWAP 20
+/* ro.lmk.thrashing_limit property defaults */
+#define DEF_THRASHING_LOWRAM 30
+#define DEF_THRASHING 100
+/* ro.lmk.thrashing_limit_decay property defaults */
+#define DEF_THRASHING_DECAY_LOWRAM 50
+#define DEF_THRASHING_DECAY 10
+/* ro.lmk.psi_partial_stall_ms property defaults */
+#define DEF_PARTIAL_STALL_LOWRAM 200
+#define DEF_PARTIAL_STALL 70
+/* ro.lmk.psi_complete_stall_ms property defaults */
+#define DEF_COMPLETE_STALL 700
+
/* default to old in-kernel interface if no memory pressure events */
static bool use_inkernel_interface = true;
static bool has_inkernel_module;
@@ -159,7 +179,12 @@
static bool use_minfree_levels;
static bool per_app_memcg;
static int swap_free_low_percentage;
+static int psi_partial_stall_ms;
+static int psi_complete_stall_ms;
+static int thrashing_limit_pct;
+static int thrashing_limit_decay_pct;
static bool use_psi_monitors = false;
+static struct kernel_poll_info kpoll_info;
static struct psi_threshold psi_thresholds[VMPRESS_LEVEL_COUNT] = {
{ PSI_SOME, 70 }, /* 70ms out of 1sec for partial stall */
{ PSI_SOME, 100 }, /* 100ms out of 1sec for partial stall */
@@ -168,10 +193,30 @@
static android_log_context ctx;
+enum polling_update {
+ POLLING_DO_NOT_CHANGE,
+ POLLING_START,
+ POLLING_STOP,
+};
+
+/*
+ * Data used for periodic polling for the memory state of the device.
+ * Note that when system is not polling poll_handler is set to NULL,
+ * when polling starts poll_handler gets set and is reset back to
+ * NULL when polling stops.
+ */
+struct polling_params {
+ struct event_handler_info* poll_handler;
+ struct timespec poll_start_tm;
+ struct timespec last_poll_tm;
+ int polling_interval_ms;
+ enum polling_update update;
+};
+
/* data required to handle events */
struct event_handler_info {
int data;
- void (*handler)(int data, uint32_t events);
+ void (*handler)(int data, uint32_t events, struct polling_params *poll_params);
};
/* data required to handle socket events */
@@ -204,37 +249,99 @@
static int lowmem_targets_size;
/* Fields to parse in /proc/zoneinfo */
-enum zoneinfo_field {
- ZI_NR_FREE_PAGES = 0,
- ZI_NR_FILE_PAGES,
- ZI_NR_SHMEM,
- ZI_NR_UNEVICTABLE,
- ZI_WORKINGSET_REFAULT,
- ZI_HIGH,
- ZI_FIELD_COUNT
+/* zoneinfo per-zone fields */
+enum zoneinfo_zone_field {
+ ZI_ZONE_NR_FREE_PAGES = 0,
+ ZI_ZONE_MIN,
+ ZI_ZONE_LOW,
+ ZI_ZONE_HIGH,
+ ZI_ZONE_PRESENT,
+ ZI_ZONE_NR_FREE_CMA,
+ ZI_ZONE_FIELD_COUNT
};
-static const char* const zoneinfo_field_names[ZI_FIELD_COUNT] = {
+static const char* const zoneinfo_zone_field_names[ZI_ZONE_FIELD_COUNT] = {
"nr_free_pages",
- "nr_file_pages",
- "nr_shmem",
- "nr_unevictable",
- "workingset_refault",
+ "min",
+ "low",
"high",
+ "present",
+ "nr_free_cma",
};
-union zoneinfo {
+/* zoneinfo per-zone special fields */
+enum zoneinfo_zone_spec_field {
+ ZI_ZONE_SPEC_PROTECTION = 0,
+ ZI_ZONE_SPEC_PAGESETS,
+ ZI_ZONE_SPEC_FIELD_COUNT,
+};
+
+static const char* const zoneinfo_zone_spec_field_names[ZI_ZONE_SPEC_FIELD_COUNT] = {
+ "protection:",
+ "pagesets",
+};
+
+/* see __MAX_NR_ZONES definition in kernel mmzone.h */
+#define MAX_NR_ZONES 6
+
+union zoneinfo_zone_fields {
struct {
int64_t nr_free_pages;
- int64_t nr_file_pages;
- int64_t nr_shmem;
- int64_t nr_unevictable;
- int64_t workingset_refault;
+ int64_t min;
+ int64_t low;
int64_t high;
- /* fields below are calculated rather than read from the file */
- int64_t totalreserve_pages;
+ int64_t present;
+ int64_t nr_free_cma;
} field;
- int64_t arr[ZI_FIELD_COUNT];
+ int64_t arr[ZI_ZONE_FIELD_COUNT];
+};
+
+struct zoneinfo_zone {
+ union zoneinfo_zone_fields fields;
+ int64_t protection[MAX_NR_ZONES];
+ int64_t max_protection;
+};
+
+/* zoneinfo per-node fields */
+enum zoneinfo_node_field {
+ ZI_NODE_NR_INACTIVE_FILE = 0,
+ ZI_NODE_NR_ACTIVE_FILE,
+ ZI_NODE_WORKINGSET_REFAULT,
+ ZI_NODE_FIELD_COUNT
+};
+
+static const char* const zoneinfo_node_field_names[ZI_NODE_FIELD_COUNT] = {
+ "nr_inactive_file",
+ "nr_active_file",
+ "workingset_refault",
+};
+
+union zoneinfo_node_fields {
+ struct {
+ int64_t nr_inactive_file;
+ int64_t nr_active_file;
+ int64_t workingset_refault;
+ } field;
+ int64_t arr[ZI_NODE_FIELD_COUNT];
+};
+
+struct zoneinfo_node {
+ int id;
+ int zone_count;
+ struct zoneinfo_zone zones[MAX_NR_ZONES];
+ union zoneinfo_node_fields fields;
+};
+
+/* for now two memory nodes is more than enough */
+#define MAX_NR_NODES 2
+
+struct zoneinfo {
+ int node_count;
+ struct zoneinfo_node nodes[MAX_NR_NODES];
+ int64_t totalreserve_pages;
+ int64_t total_inactive_file;
+ int64_t total_active_file;
+ int64_t total_workingset_refault;
};
/* Fields to parse in /proc/meminfo */
@@ -310,6 +417,41 @@
int64_t arr[MI_FIELD_COUNT];
};
+/* Fields to parse in /proc/vmstat */
+enum vmstat_field {
+ VS_FREE_PAGES,
+ VS_INACTIVE_FILE,
+ VS_ACTIVE_FILE,
+ VS_WORKINGSET_REFAULT,
+ VS_PGSCAN_KSWAPD,
+ VS_PGSCAN_DIRECT,
+ VS_PGSCAN_DIRECT_THROTTLE,
+ VS_FIELD_COUNT
+};
+
+static const char* const vmstat_field_names[MI_FIELD_COUNT] = {
+ "nr_free_pages",
+ "nr_inactive_file",
+ "nr_active_file",
+ "workingset_refault",
+ "pgscan_kswapd",
+ "pgscan_direct",
+ "pgscan_direct_throttle",
+};
+
+union vmstat {
+ struct {
+ int64_t nr_free_pages;
+ int64_t nr_inactive_file;
+ int64_t nr_active_file;
+ int64_t workingset_refault;
+ int64_t pgscan_kswapd;
+ int64_t pgscan_direct;
+ int64_t pgscan_direct_throttle;
+ } field;
+ int64_t arr[VS_FIELD_COUNT];
+};
+
enum field_match_result {
NO_MATCH,
PARSE_FAIL,
@@ -334,11 +476,6 @@
int fd;
};
-#ifdef LMKD_LOG_STATS
-static bool enable_stats_log;
-static android_log_context log_ctx;
-#endif
-
#define PIDHASH_SZ 1024
static struct proc *pidhash[PIDHASH_SZ];
#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
@@ -362,8 +499,9 @@
/* PAGE_SIZE / 1024 */
static long page_k;
-static char* proc_get_name(int pid);
-static void poll_kernel();
+static int clamp(int low, int high, int value) {
+ return max(min(value, high), low);
+}
static bool parse_int64(const char* str, int64_t* ret) {
char* endptr;
@@ -375,20 +513,25 @@
return true;
}
+static int find_field(const char* name, const char* const field_names[], int field_count) {
+ for (int i = 0; i < field_count; i++) {
+ if (!strcmp(name, field_names[i])) {
+ return i;
+ }
+ }
+ return -1;
+}
+
static enum field_match_result match_field(const char* cp, const char* ap,
const char* const field_names[],
int field_count, int64_t* field,
int *field_idx) {
- int64_t val;
- int i;
-
- for (i = 0; i < field_count; i++) {
- if (!strcmp(cp, field_names[i])) {
- *field_idx = i;
- return parse_int64(ap, field) ? PARSE_SUCCESS : PARSE_FAIL;
- }
+ int i = find_field(cp, field_names, field_count);
+ if (i < 0) {
+ return NO_MATCH;
}
- return NO_MATCH;
+ *field_idx = i;
+ return parse_int64(ap, field) ? PARSE_SUCCESS : PARSE_FAIL;
}
/*
@@ -424,28 +567,50 @@
* memory pressure to minimize file opening which by itself requires kernel
* memory allocation and might result in a stall on memory stressed system.
*/
-static int reread_file(struct reread_data *data, char *buf, size_t buf_size) {
+static char *reread_file(struct reread_data *data) {
+ /* start with page-size buffer and increase if needed */
+ static ssize_t buf_size = PAGE_SIZE;
+ static char *new_buf, *buf = NULL;
ssize_t size;
if (data->fd == -1) {
- data->fd = open(data->filename, O_RDONLY | O_CLOEXEC);
- if (data->fd == -1) {
+ /* First-time buffer initialization */
+ if (!buf && (buf = malloc(buf_size)) == NULL) {
+ return NULL;
+ }
+
+ data->fd = TEMP_FAILURE_RETRY(open(data->filename, O_RDONLY | O_CLOEXEC));
+ if (data->fd < 0) {
ALOGE("%s open: %s", data->filename, strerror(errno));
- return -1;
+ return NULL;
}
}
- size = read_all(data->fd, buf, buf_size - 1);
- if (size < 0) {
- ALOGE("%s read: %s", data->filename, strerror(errno));
- close(data->fd);
- data->fd = -1;
- return -1;
+ while (true) {
+ size = read_all(data->fd, buf, buf_size - 1);
+ if (size < 0) {
+ ALOGE("%s read: %s", data->filename, strerror(errno));
+ close(data->fd);
+ data->fd = -1;
+ return NULL;
+ }
+ if (size < buf_size - 1) {
+ break;
+ }
+ /*
+ * Since we are reading /proc files we can't use fstat to find out
+ * the real size of the file. Double the buffer size and keep retrying.
+ */
+ if ((new_buf = realloc(buf, buf_size * 2)) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ buf = new_buf;
+ buf_size *= 2;
}
- ALOG_ASSERT((size_t)size < buf_size - 1, "%s too large", data->filename);
buf[size] = 0;
- return 0;
+ return buf;
}
static struct proc *pid_lookup(int pid) {
@@ -598,6 +763,60 @@
return (int)tgid;
}
+static int proc_get_size(int pid) {
+ char path[PATH_MAX];
+ char line[LINE_MAX];
+ int fd;
+ int rss = 0;
+ int total;
+ ssize_t ret;
+
+ /* gid containing AID_READPROC required */
+ snprintf(path, PATH_MAX, "/proc/%d/statm", pid);
+ fd = open(path, O_RDONLY | O_CLOEXEC);
+ if (fd == -1)
+ return -1;
+
+ ret = read_all(fd, line, sizeof(line) - 1);
+ if (ret < 0) {
+ close(fd);
+ return -1;
+ }
+
+ sscanf(line, "%d %d ", &total, &rss);
+ close(fd);
+ return rss;
+}
+
+static char *proc_get_name(int pid) {
+ char path[PATH_MAX];
+ static char line[LINE_MAX];
+ int fd;
+ char *cp;
+ ssize_t ret;
+
+ /* gid containing AID_READPROC required */
+ snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid);
+ fd = open(path, O_RDONLY | O_CLOEXEC);
+ if (fd == -1) {
+ return NULL;
+ }
+ ret = read_all(fd, line, sizeof(line) - 1);
+ close(fd);
+ if (ret < 0) {
+ return NULL;
+ }
+
+ cp = strchr(line, ' ');
+ if (cp) {
+ *cp = '\0';
+ } else {
+ line[ret] = '\0';
+ }
+
+ return line;
+}
+
static void cmd_procprio(LMKD_CTRL_PACKET packet) {
struct proc *procp;
char path[80];
@@ -637,9 +856,7 @@
}
if (use_inkernel_interface) {
-#ifdef LMKD_LOG_STATS
- stats_store_taskname(params.pid, proc_get_name(params.pid));
-#endif
+ stats_store_taskname(params.pid, proc_get_name(params.pid), kpoll_info.poll_fd);
return;
}
@@ -710,16 +927,7 @@
struct lmk_procremove params;
if (use_inkernel_interface) {
-#ifdef LMKD_LOG_STATS
- /* Perform an extra check before the pid is removed, after which it
- * will be impossible for poll_kernel to get the taskname. poll_kernel()
- * is potentially a long-running blocking function; however this method
- * handles AMS requests but does not block AMS.*/
- if (enable_stats_log) {
- poll_kernel();
- }
- stats_remove_taskname(params.pid);
-#endif
+ stats_remove_taskname(params.pid, kpoll_info.poll_fd);
return;
}
@@ -737,9 +945,7 @@
struct proc *next;
if (use_inkernel_interface) {
-#ifdef LMKD_LOG_STATS
stats_purge_tasknames();
-#endif
return;
}
@@ -1002,7 +1208,8 @@
ALOGE("Wrong control socket read length cmd=%d len=%d", cmd, len);
}
-static void ctrl_data_handler(int data, uint32_t events) {
+static void ctrl_data_handler(int data, uint32_t events,
+ struct polling_params *poll_params __unused) {
if (events & EPOLLIN) {
ctrl_command_handler(data);
}
@@ -1017,7 +1224,8 @@
return -1;
}
-static void ctrl_connect_handler(int data __unused, uint32_t events __unused) {
+static void ctrl_connect_handler(int data __unused, uint32_t events __unused,
+ struct polling_params *poll_params __unused) {
struct epoll_event epev;
int free_dscock_idx = get_free_dsock();
@@ -1055,174 +1263,202 @@
maxevents++;
}
-#ifdef LMKD_LOG_STATS
-static void memory_stat_parse_line(char* line, struct memory_stat* mem_st) {
- char key[LINE_MAX + 1];
- int64_t value;
-
- sscanf(line, "%" STRINGIFY(LINE_MAX) "s %" SCNd64 "", key, &value);
-
- if (strcmp(key, "total_") < 0) {
- return;
- }
-
- if (!strcmp(key, "total_pgfault"))
- mem_st->pgfault = value;
- else if (!strcmp(key, "total_pgmajfault"))
- mem_st->pgmajfault = value;
- else if (!strcmp(key, "total_rss"))
- mem_st->rss_in_bytes = value;
- else if (!strcmp(key, "total_cache"))
- mem_st->cache_in_bytes = value;
- else if (!strcmp(key, "total_swap"))
- mem_st->swap_in_bytes = value;
-}
-
-static int memory_stat_from_cgroup(struct memory_stat* mem_st, int pid, uid_t uid) {
- FILE *fp;
- char buf[PATH_MAX];
-
- snprintf(buf, sizeof(buf), MEMCG_PROCESS_MEMORY_STAT_PATH, uid, pid);
-
- fp = fopen(buf, "r");
-
- if (fp == NULL) {
- ALOGE("%s open failed: %s", buf, strerror(errno));
- return -1;
- }
-
- while (fgets(buf, PAGE_SIZE, fp) != NULL) {
- memory_stat_parse_line(buf, mem_st);
- }
- fclose(fp);
-
- return 0;
-}
-
-static int memory_stat_from_procfs(struct memory_stat* mem_st, int pid) {
- char path[PATH_MAX];
- char buffer[PROC_STAT_BUFFER_SIZE];
- int fd, ret;
-
- snprintf(path, sizeof(path), PROC_STAT_FILE_PATH, pid);
- if ((fd = open(path, O_RDONLY | O_CLOEXEC)) < 0) {
- ALOGE("%s open failed: %s", path, strerror(errno));
- return -1;
- }
-
- ret = read(fd, buffer, sizeof(buffer));
- if (ret < 0) {
- ALOGE("%s read failed: %s", path, strerror(errno));
- close(fd);
- return -1;
- }
- close(fd);
-
- // field 10 is pgfault
- // field 12 is pgmajfault
- // field 22 is starttime
- // field 24 is rss_in_pages
- int64_t pgfault = 0, pgmajfault = 0, starttime = 0, rss_in_pages = 0;
- if (sscanf(buffer,
- "%*u %*s %*s %*d %*d %*d %*d %*d %*d %" SCNd64 " %*d "
- "%" SCNd64 " %*d %*u %*u %*d %*d %*d %*d %*d %*d "
- "%" SCNd64 " %*d %" SCNd64 "",
- &pgfault, &pgmajfault, &starttime, &rss_in_pages) != 4) {
- return -1;
- }
- mem_st->pgfault = pgfault;
- mem_st->pgmajfault = pgmajfault;
- mem_st->rss_in_bytes = (rss_in_pages * PAGE_SIZE);
- mem_st->process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
- return 0;
-}
-#endif
-
-/* /prop/zoneinfo parsing routines */
-static int64_t zoneinfo_parse_protection(char *cp) {
+/*
+ * /proc/zoneinfo parsing routines
+ * Expected file format is:
+ *
+ * Node <node_id>, zone <zone_name>
+ * (
+ * per-node stats
+ * (<per-node field name> <value>)+
+ * )?
+ * (pages free <value>
+ * (<per-zone field name> <value>)+
+ * pagesets
+ * (<unused fields>)*
+ * )+
+ * ...
+ */
+static void zoneinfo_parse_protection(char *buf, struct zoneinfo_zone *zone) {
+ int zone_idx;
int64_t max = 0;
- long long zoneval;
char *save_ptr;
- for (cp = strtok_r(cp, "(), ", &save_ptr); cp;
- cp = strtok_r(NULL, "), ", &save_ptr)) {
- zoneval = strtoll(cp, &cp, 0);
+ for (buf = strtok_r(buf, "(), ", &save_ptr), zone_idx = 0;
+ buf && zone_idx < MAX_NR_ZONES;
+ buf = strtok_r(NULL, "), ", &save_ptr), zone_idx++) {
+ long long zoneval = strtoll(buf, &buf, 0);
if (zoneval > max) {
max = (zoneval > INT64_MAX) ? INT64_MAX : zoneval;
}
+ zone->protection[zone_idx] = zoneval;
}
-
- return max;
+ zone->max_protection = max;
}
-static bool zoneinfo_parse_line(char *line, union zoneinfo *zi) {
- char *cp = line;
- char *ap;
- char *save_ptr;
- int64_t val;
- int field_idx;
+static int zoneinfo_parse_zone(char **buf, struct zoneinfo_zone *zone) {
+ for (char *line = strtok_r(NULL, "\n", buf); line;
+ line = strtok_r(NULL, "\n", buf)) {
+ char *cp;
+ char *ap;
+ char *save_ptr;
+ int64_t val;
+ int field_idx;
+ enum field_match_result match_res;
- cp = strtok_r(line, " ", &save_ptr);
- if (!cp) {
- return true;
- }
-
- if (!strcmp(cp, "protection:")) {
- ap = strtok_r(NULL, ")", &save_ptr);
- } else {
- ap = strtok_r(NULL, " ", &save_ptr);
- }
-
- if (!ap) {
- return true;
- }
-
- switch (match_field(cp, ap, zoneinfo_field_names,
- ZI_FIELD_COUNT, &val, &field_idx)) {
- case (PARSE_SUCCESS):
- zi->arr[field_idx] += val;
- break;
- case (NO_MATCH):
- if (!strcmp(cp, "protection:")) {
- zi->field.totalreserve_pages +=
- zoneinfo_parse_protection(ap);
+ cp = strtok_r(line, " ", &save_ptr);
+ if (!cp) {
+ return false;
}
- break;
- case (PARSE_FAIL):
- default:
- return false;
+
+ field_idx = find_field(cp, zoneinfo_zone_spec_field_names, ZI_ZONE_SPEC_FIELD_COUNT);
+ if (field_idx >= 0) {
+ /* special field */
+ if (field_idx == ZI_ZONE_SPEC_PAGESETS) {
+ /* no mode fields we are interested in */
+ return true;
+ }
+
+ /* protection field */
+ ap = strtok_r(NULL, ")", &save_ptr);
+ if (ap) {
+ zoneinfo_parse_protection(ap, zone);
+ }
+ continue;
+ }
+
+ ap = strtok_r(NULL, " ", &save_ptr);
+ if (!ap) {
+ continue;
+ }
+
+ match_res = match_field(cp, ap, zoneinfo_zone_field_names, ZI_ZONE_FIELD_COUNT,
+ &val, &field_idx);
+ if (match_res == PARSE_FAIL) {
+ return false;
+ }
+ if (match_res == PARSE_SUCCESS) {
+ zone->fields.arr[field_idx] = val;
+ }
+ if (field_idx == ZI_ZONE_PRESENT && val == 0) {
+ /* zone is not populated, stop parsing it */
+ return true;
+ }
}
- return true;
+ return false;
}
-static int zoneinfo_parse(union zoneinfo *zi) {
+static int zoneinfo_parse_node(char **buf, struct zoneinfo_node *node) {
+ int fields_to_match = ZI_NODE_FIELD_COUNT;
+
+ for (char *line = strtok_r(NULL, "\n", buf); line;
+ line = strtok_r(NULL, "\n", buf)) {
+ char *cp;
+ char *ap;
+ char *save_ptr;
+ int64_t val;
+ int field_idx;
+ enum field_match_result match_res;
+
+ cp = strtok_r(line, " ", &save_ptr);
+ if (!cp) {
+ return false;
+ }
+
+ ap = strtok_r(NULL, " ", &save_ptr);
+ if (!ap) {
+ return false;
+ }
+
+ match_res = match_field(cp, ap, zoneinfo_node_field_names, ZI_NODE_FIELD_COUNT,
+ &val, &field_idx);
+ if (match_res == PARSE_FAIL) {
+ return false;
+ }
+ if (match_res == PARSE_SUCCESS) {
+ node->fields.arr[field_idx] = val;
+ fields_to_match--;
+ if (!fields_to_match) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+static int zoneinfo_parse(struct zoneinfo *zi) {
static struct reread_data file_data = {
.filename = ZONEINFO_PATH,
.fd = -1,
};
- char buf[PAGE_SIZE];
+ char *buf;
char *save_ptr;
char *line;
+ char zone_name[LINE_MAX];
+ struct zoneinfo_node *node = NULL;
+ int node_idx = 0;
+ int zone_idx = 0;
- memset(zi, 0, sizeof(union zoneinfo));
+ memset(zi, 0, sizeof(struct zoneinfo));
- if (reread_file(&file_data, buf, sizeof(buf)) < 0) {
+ if ((buf = reread_file(&file_data)) == NULL) {
return -1;
}
for (line = strtok_r(buf, "\n", &save_ptr); line;
line = strtok_r(NULL, "\n", &save_ptr)) {
- if (!zoneinfo_parse_line(line, zi)) {
- ALOGE("%s parse error", file_data.filename);
- return -1;
+ int node_id;
+ if (sscanf(line, "Node %d, zone %" STRINGIFY(LINE_MAX) "s", &node_id, zone_name) == 2) {
+ if (!node || node->id != node_id) {
+ /* new node is found */
+ if (node) {
+ node->zone_count = zone_idx + 1;
+ node_idx++;
+ if (node_idx == MAX_NR_NODES) {
+ /* max node count exceeded */
+ ALOGE("%s parse error", file_data.filename);
+ return -1;
+ }
+ }
+ node = &zi->nodes[node_idx];
+ node->id = node_id;
+ zone_idx = 0;
+ if (!zoneinfo_parse_node(&save_ptr, node)) {
+ ALOGE("%s parse error", file_data.filename);
+ return -1;
+ }
+ } else {
+ /* new zone is found */
+ zone_idx++;
+ }
+ if (!zoneinfo_parse_zone(&save_ptr, &node->zones[zone_idx])) {
+ ALOGE("%s parse error", file_data.filename);
+ return -1;
+ }
}
}
- zi->field.totalreserve_pages += zi->field.high;
+ if (!node) {
+ ALOGE("%s parse error", file_data.filename);
+ return -1;
+ }
+ node->zone_count = zone_idx + 1;
+ zi->node_count = node_idx + 1;
+ /* calculate totals fields */
+ for (node_idx = 0; node_idx < zi->node_count; node_idx++) {
+ node = &zi->nodes[node_idx];
+ for (zone_idx = 0; zone_idx < node->zone_count; zone_idx++) {
+ struct zoneinfo_zone *zone = &zi->nodes[node_idx].zones[zone_idx];
+ zi->totalreserve_pages += zone->max_protection + zone->fields.field.high;
+ }
+ zi->total_inactive_file += node->fields.field.nr_inactive_file;
+ zi->total_active_file += node->fields.field.nr_active_file;
+ zi->total_workingset_refault += node->fields.field.workingset_refault;
+ }
return 0;
}
-/* /prop/meminfo parsing routines */
+/* /proc/meminfo parsing routines */
static bool meminfo_parse_line(char *line, union meminfo *mi) {
char *cp = line;
char *ap;
@@ -1254,13 +1490,13 @@
.filename = MEMINFO_PATH,
.fd = -1,
};
- char buf[PAGE_SIZE];
+ char *buf;
char *save_ptr;
char *line;
memset(mi, 0, sizeof(union meminfo));
- if (reread_file(&file_data, buf, sizeof(buf)) < 0) {
+ if ((buf = reread_file(&file_data)) == NULL) {
return -1;
}
@@ -1277,6 +1513,59 @@
return 0;
}
+/* /proc/vmstat parsing routines */
+static bool vmstat_parse_line(char *line, union vmstat *vs) {
+ char *cp;
+ char *ap;
+ char *save_ptr;
+ int64_t val;
+ int field_idx;
+ enum field_match_result match_res;
+
+ cp = strtok_r(line, " ", &save_ptr);
+ if (!cp) {
+ return false;
+ }
+
+ ap = strtok_r(NULL, " ", &save_ptr);
+ if (!ap) {
+ return false;
+ }
+
+ match_res = match_field(cp, ap, vmstat_field_names, VS_FIELD_COUNT,
+ &val, &field_idx);
+ if (match_res == PARSE_SUCCESS) {
+ vs->arr[field_idx] = val;
+ }
+ return (match_res != PARSE_FAIL);
+}
+
+static int vmstat_parse(union vmstat *vs) {
+ static struct reread_data file_data = {
+ .filename = VMSTAT_PATH,
+ .fd = -1,
+ };
+ char *buf;
+ char *save_ptr;
+ char *line;
+
+ memset(vs, 0, sizeof(union vmstat));
+
+ if ((buf = reread_file(&file_data)) == NULL) {
+ return -1;
+ }
+
+ for (line = strtok_r(buf, "\n", &save_ptr); line;
+ line = strtok_r(NULL, "\n", &save_ptr)) {
+ if (!vmstat_parse_line(line, vs)) {
+ ALOGE("%s parse error", file_data.filename);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
static void meminfo_log(union meminfo *mi) {
for (int field_idx = 0; field_idx < MI_FIELD_COUNT; field_idx++) {
android_log_write_int32(ctx, (int32_t)min(mi->arr[field_idx] * page_k, INT32_MAX));
@@ -1286,60 +1575,6 @@
android_log_reset(ctx);
}
-static int proc_get_size(int pid) {
- char path[PATH_MAX];
- char line[LINE_MAX];
- int fd;
- int rss = 0;
- int total;
- ssize_t ret;
-
- /* gid containing AID_READPROC required */
- snprintf(path, PATH_MAX, "/proc/%d/statm", pid);
- fd = open(path, O_RDONLY | O_CLOEXEC);
- if (fd == -1)
- return -1;
-
- ret = read_all(fd, line, sizeof(line) - 1);
- if (ret < 0) {
- close(fd);
- return -1;
- }
-
- sscanf(line, "%d %d ", &total, &rss);
- close(fd);
- return rss;
-}
-
-static char *proc_get_name(int pid) {
- char path[PATH_MAX];
- static char line[LINE_MAX];
- int fd;
- char *cp;
- ssize_t ret;
-
- /* gid containing AID_READPROC required */
- snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid);
- fd = open(path, O_RDONLY | O_CLOEXEC);
- if (fd == -1) {
- return NULL;
- }
- ret = read_all(fd, line, sizeof(line) - 1);
- close(fd);
- if (ret < 0) {
- return NULL;
- }
-
- cp = strchr(line, ' ');
- if (cp) {
- *cp = '\0';
- } else {
- line[ret] = '\0';
- }
-
- return line;
-}
-
static struct proc *proc_adj_lru(int oomadj) {
return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
}
@@ -1405,7 +1640,7 @@
static int last_killed_pid = -1;
/* Kill one process specified by procp. Returns the size of the process killed */
-static int kill_one_process(struct proc* procp, int min_oom_score) {
+static int kill_one_process(struct proc* procp, int min_oom_score, const char *reason) {
int pid = procp->pid;
uid_t uid = procp->uid;
int tgid;
@@ -1413,14 +1648,7 @@
int tasksize;
int r;
int result = -1;
-
-#ifdef LMKD_LOG_STATS
- struct memory_stat mem_st = {};
- int memory_stat_parse_result = -1;
-#else
- /* To prevent unused parameter warning */
- (void)(min_oom_score);
-#endif
+ struct memory_stat *mem_st;
tgid = proc_get_tgid(pid);
if (tgid >= 0 && tgid != pid) {
@@ -1438,50 +1666,39 @@
goto out;
}
-#ifdef LMKD_LOG_STATS
- if (enable_stats_log) {
- if (per_app_memcg) {
- memory_stat_parse_result = memory_stat_from_cgroup(&mem_st, pid, uid);
- } else {
- memory_stat_parse_result = memory_stat_from_procfs(&mem_st, pid);
- }
- }
-#endif
+ mem_st = stats_read_memory_stat(per_app_memcg, pid, uid);
TRACE_KILL_START(pid);
/* CAP_KILL required */
r = kill(pid, SIGKILL);
- set_process_group_and_prio(pid, SP_FOREGROUND, ANDROID_PRIORITY_HIGHEST);
-
- inc_killcnt(procp->oomadj);
- ALOGE("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB", taskname, pid, uid, procp->oomadj,
- tasksize * page_k);
-
TRACE_KILL_END();
- last_killed_pid = pid;
-
if (r) {
ALOGE("kill(%d): errno=%d", pid, errno);
+ /* Delete process record even when we fail to kill so that we don't get stuck on it */
goto out;
- } else {
-#ifdef LMKD_LOG_STATS
- if (memory_stat_parse_result == 0) {
- stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname,
- procp->oomadj, mem_st.pgfault, mem_st.pgmajfault, mem_st.rss_in_bytes,
- mem_st.cache_in_bytes, mem_st.swap_in_bytes, mem_st.process_start_time_ns,
- min_oom_score);
- } else if (enable_stats_log) {
- stats_write_lmk_kill_occurred(log_ctx, LMK_KILL_OCCURRED, uid, taskname, procp->oomadj,
- -1, -1, tasksize * BYTES_IN_KILOBYTE, -1, -1, -1,
- min_oom_score);
- }
-#endif
- result = tasksize;
}
+ set_process_group_and_prio(pid, SP_FOREGROUND, ANDROID_PRIORITY_HIGHEST);
+
+ inc_killcnt(procp->oomadj);
+ if (reason) {
+ ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB; reason: %s", taskname, pid,
+ uid, procp->oomadj, tasksize * page_k, reason);
+ } else {
+ ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB", taskname, pid,
+ uid, procp->oomadj, tasksize * page_k);
+ }
+
+ last_killed_pid = pid;
+
+ stats_write_lmk_kill_occurred(LMK_KILL_OCCURRED, uid, taskname,
+ procp->oomadj, min_oom_score, tasksize, mem_st);
+
+ result = tasksize;
+
out:
/*
* WARNING: After pid_remove() procp is freed and can't be used!
@@ -1495,13 +1712,10 @@
* Find one process to kill at or above the given oom_adj level.
* Returns size of the killed process.
*/
-static int find_and_kill_process(int min_score_adj) {
+static int find_and_kill_process(int min_score_adj, const char *reason) {
int i;
int killed_size = 0;
-
-#ifdef LMKD_LOG_STATS
bool lmk_state_change_start = false;
-#endif
for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
struct proc *procp;
@@ -1513,15 +1727,13 @@
if (!procp)
break;
- killed_size = kill_one_process(procp, min_score_adj);
+ killed_size = kill_one_process(procp, min_score_adj, reason);
if (killed_size >= 0) {
-#ifdef LMKD_LOG_STATS
- if (enable_stats_log && !lmk_state_change_start) {
+ if (!lmk_state_change_start) {
lmk_state_change_start = true;
- stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED,
+ stats_write_lmk_state_changed(LMK_STATE_CHANGED,
LMK_STATE_CHANGE_START);
}
-#endif
break;
}
}
@@ -1530,11 +1742,9 @@
}
}
-#ifdef LMKD_LOG_STATS
- if (enable_stats_log && lmk_state_change_start) {
- stats_write_lmk_state_changed(log_ctx, LMK_STATE_CHANGED, LMK_STATE_CHANGE_STOP);
+ if (lmk_state_change_start) {
+ stats_write_lmk_state_changed(LMK_STATE_CHANGED, LMK_STATE_CHANGE_STOP);
}
-#endif
return killed_size;
}
@@ -1542,9 +1752,9 @@
static int64_t get_memory_usage(struct reread_data *file_data) {
int ret;
int64_t mem_usage;
- char buf[32];
+ char *buf;
- if (reread_file(file_data, buf, sizeof(buf)) < 0) {
+ if ((buf = reread_file(file_data)) == NULL) {
return -1;
}
@@ -1613,14 +1823,277 @@
return false;
}
-static void mp_event_common(int data, uint32_t events __unused) {
+enum zone_watermark {
+ WMARK_MIN = 0,
+ WMARK_LOW,
+ WMARK_HIGH,
+ WMARK_NONE
+};
+
+struct zone_watermarks {
+ long high_wmark;
+ long low_wmark;
+ long min_wmark;
+};
+
+/*
+ * Returns lowest breached watermark or WMARK_NONE.
+ */
+static enum zone_watermark get_lowest_watermark(union meminfo *mi,
+ struct zone_watermarks *watermarks)
+{
+ int64_t nr_free_pages = mi->field.nr_free_pages - mi->field.cma_free;
+
+ if (nr_free_pages < watermarks->min_wmark) {
+ return WMARK_MIN;
+ }
+ if (nr_free_pages < watermarks->low_wmark) {
+ return WMARK_LOW;
+ }
+ if (nr_free_pages < watermarks->high_wmark) {
+ return WMARK_HIGH;
+ }
+ return WMARK_NONE;
+}
+
+void calc_zone_watermarks(struct zoneinfo *zi, struct zone_watermarks *watermarks) {
+ memset(watermarks, 0, sizeof(struct zone_watermarks));
+
+ for (int node_idx = 0; node_idx < zi->node_count; node_idx++) {
+ struct zoneinfo_node *node = &zi->nodes[node_idx];
+ for (int zone_idx = 0; zone_idx < node->zone_count; zone_idx++) {
+ struct zoneinfo_zone *zone = &node->zones[zone_idx];
+
+ if (!zone->fields.field.present) {
+ continue;
+ }
+
+ watermarks->high_wmark += zone->max_protection + zone->fields.field.high;
+ watermarks->low_wmark += zone->max_protection + zone->fields.field.low;
+ watermarks->min_wmark += zone->max_protection + zone->fields.field.min;
+ }
+ }
+}
+
+static void mp_event_psi(int data, uint32_t events, struct polling_params *poll_params) {
+ enum kill_reasons {
+ NONE = -1, /* To denote no kill condition */
+ PRESSURE_AFTER_KILL = 0,
+ NOT_RESPONDING,
+ LOW_SWAP_AND_THRASHING,
+ LOW_MEM_AND_SWAP,
+ LOW_MEM_AND_THRASHING,
+ DIRECT_RECL_AND_THRASHING,
+ KILL_REASON_COUNT
+ };
+ enum reclaim_state {
+ NO_RECLAIM = 0,
+ KSWAPD_RECLAIM,
+ DIRECT_RECLAIM,
+ };
+ static int64_t init_ws_refault;
+ static int64_t base_file_lru;
+ static int64_t init_pgscan_kswapd;
+ static int64_t init_pgscan_direct;
+ static int64_t swap_low_threshold;
+ static bool killing;
+ static int thrashing_limit;
+ static bool in_reclaim;
+ static struct zone_watermarks watermarks;
+ static struct timespec wmark_update_tm;
+
+ union meminfo mi;
+ union vmstat vs;
+ struct timespec curr_tm;
+ int64_t thrashing = 0;
+ bool swap_is_low = false;
+ enum vmpressure_level level = (enum vmpressure_level)data;
+ enum kill_reasons kill_reason = NONE;
+ bool cycle_after_kill = false;
+ enum reclaim_state reclaim = NO_RECLAIM;
+ enum zone_watermark wmark = WMARK_NONE;
+ char kill_desc[LINE_MAX];
+ bool cut_thrashing_limit = false;
+ int min_score_adj = 0;
+
+ /* Skip while still killing a process */
+ if (is_kill_pending()) {
+ /* TODO: replace this quick polling with pidfd polling if kernel supports */
+ goto no_kill;
+ }
+
+ if (clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm) != 0) {
+ ALOGE("Failed to get current time");
+ return;
+ }
+
+ if (vmstat_parse(&vs) < 0) {
+ ALOGE("Failed to parse vmstat!");
+ return;
+ }
+
+ if (meminfo_parse(&mi) < 0) {
+ ALOGE("Failed to parse meminfo!");
+ return;
+ }
+
+ /* Reset states after process got killed */
+ if (killing) {
+ killing = false;
+ cycle_after_kill = true;
+ /* Reset file-backed pagecache size and refault amounts after a kill */
+ base_file_lru = vs.field.nr_inactive_file + vs.field.nr_active_file;
+ init_ws_refault = vs.field.workingset_refault;
+ }
+
+ /* Check free swap levels */
+ if (swap_free_low_percentage) {
+ if (!swap_low_threshold) {
+ swap_low_threshold = mi.field.total_swap * swap_free_low_percentage / 100;
+ }
+ swap_is_low = mi.field.free_swap < swap_low_threshold;
+ }
+
+ /* Identify reclaim state */
+ if (vs.field.pgscan_direct > init_pgscan_direct) {
+ init_pgscan_direct = vs.field.pgscan_direct;
+ init_pgscan_kswapd = vs.field.pgscan_kswapd;
+ reclaim = DIRECT_RECLAIM;
+ } else if (vs.field.pgscan_kswapd > init_pgscan_kswapd) {
+ init_pgscan_kswapd = vs.field.pgscan_kswapd;
+ reclaim = KSWAPD_RECLAIM;
+ } else {
+ in_reclaim = false;
+ /* Skip if system is not reclaiming */
+ goto no_kill;
+ }
+
+ if (!in_reclaim) {
+ /* Record file-backed pagecache size when entering reclaim cycle */
+ base_file_lru = vs.field.nr_inactive_file + vs.field.nr_active_file;
+ init_ws_refault = vs.field.workingset_refault;
+ thrashing_limit = thrashing_limit_pct;
+ } else {
+ /* Calculate what % of the file-backed pagecache refaulted so far */
+ thrashing = (vs.field.workingset_refault - init_ws_refault) * 100 / base_file_lru;
+ }
+ in_reclaim = true;
+
+ /*
+ * Refresh watermarks once per min in case user updated one of the margins.
+ * TODO: b/140521024 replace this periodic update with an API for AMS to notify LMKD
+ * that zone watermarks were changed by the system software.
+ */
+ if (watermarks.high_wmark == 0 || get_time_diff_ms(&wmark_update_tm, &curr_tm) > 60000) {
+ struct zoneinfo zi;
+
+ if (zoneinfo_parse(&zi) < 0) {
+ ALOGE("Failed to parse zoneinfo!");
+ return;
+ }
+
+ calc_zone_watermarks(&zi, &watermarks);
+ wmark_update_tm = curr_tm;
+ }
+
+ /* Find out which watermark is breached if any */
+ wmark = get_lowest_watermark(&mi, &watermarks);
+
+ /*
+ * TODO: move this logic into a separate function
+ * Decide if killing a process is necessary and record the reason
+ */
+ if (cycle_after_kill && wmark < WMARK_LOW) {
+ /*
+ * Prevent kills not freeing enough memory which might lead to OOM kill.
+ * This might happen when a process is consuming memory faster than reclaim can
+ * free even after a kill. Mostly happens when running memory stress tests.
+ */
+ kill_reason = PRESSURE_AFTER_KILL;
+ strncpy(kill_desc, "min watermark is breached even after kill", sizeof(kill_desc));
+ } else if (level == VMPRESS_LEVEL_CRITICAL && events != 0) {
+ /*
+ * Device is too busy reclaiming memory which might lead to ANR.
+ * Critical level is triggered when PSI complete stall (all tasks are blocked because
+ * of the memory congestion) breaches the configured threshold.
+ */
+ kill_reason = NOT_RESPONDING;
+ strncpy(kill_desc, "device is not responding", sizeof(kill_desc));
+ } else if (swap_is_low && thrashing > thrashing_limit_pct) {
+ /* Page cache is thrashing while swap is low */
+ kill_reason = LOW_SWAP_AND_THRASHING;
+ snprintf(kill_desc, sizeof(kill_desc), "device is low on swap (%" PRId64
+ "kB < %" PRId64 "kB) and thrashing (%" PRId64 "%%)",
+ mi.field.free_swap * page_k, swap_low_threshold * page_k, thrashing);
+ } else if (swap_is_low && wmark < WMARK_HIGH) {
+ /* Both free memory and swap are low */
+ kill_reason = LOW_MEM_AND_SWAP;
+ snprintf(kill_desc, sizeof(kill_desc), "%s watermark is breached and swap is low (%"
+ PRId64 "kB < %" PRId64 "kB)", wmark > WMARK_LOW ? "min" : "low",
+ mi.field.free_swap * page_k, swap_low_threshold * page_k);
+ } else if (wmark < WMARK_HIGH && thrashing > thrashing_limit) {
+ /* Page cache is thrashing while memory is low */
+ kill_reason = LOW_MEM_AND_THRASHING;
+ snprintf(kill_desc, sizeof(kill_desc), "%s watermark is breached and thrashing (%"
+ PRId64 "%%)", wmark > WMARK_LOW ? "min" : "low", thrashing);
+ cut_thrashing_limit = true;
+ /* Do not kill perceptible apps because of thrashing */
+ min_score_adj = PERCEPTIBLE_APP_ADJ;
+ } else if (reclaim == DIRECT_RECLAIM && thrashing > thrashing_limit) {
+ /* Page cache is thrashing while in direct reclaim (mostly happens on lowram devices) */
+ kill_reason = DIRECT_RECL_AND_THRASHING;
+ snprintf(kill_desc, sizeof(kill_desc), "device is in direct reclaim and thrashing (%"
+ PRId64 "%%)", thrashing);
+ cut_thrashing_limit = true;
+ /* Do not kill perceptible apps because of thrashing */
+ min_score_adj = PERCEPTIBLE_APP_ADJ;
+ }
+
+ /* Kill a process if necessary */
+ if (kill_reason != NONE) {
+ int pages_freed = find_and_kill_process(min_score_adj, kill_desc);
+ if (pages_freed > 0) {
+ killing = true;
+ if (cut_thrashing_limit) {
+ /*
+ * Cut thrasing limit by thrashing_limit_decay_pct percentage of the current
+ * thrashing limit until the system stops thrashing.
+ */
+ thrashing_limit = (thrashing_limit * (100 - thrashing_limit_decay_pct)) / 100;
+ }
+ }
+ meminfo_log(&mi);
+ }
+
+no_kill:
+ /*
+ * Start polling after initial PSI event;
+ * extend polling while device is in direct reclaim or process is being killed;
+ * do not extend when kswapd reclaims because that might go on for a long time
+ * without causing memory pressure
+ */
+ if (events || killing || reclaim == DIRECT_RECLAIM) {
+ poll_params->update = POLLING_START;
+ }
+
+ /* Decide the polling interval */
+ if (swap_is_low || killing) {
+ /* Fast polling during and after a kill or when swap is low */
+ poll_params->polling_interval_ms = PSI_POLL_PERIOD_SHORT_MS;
+ } else {
+ /* By default use long intervals */
+ poll_params->polling_interval_ms = PSI_POLL_PERIOD_LONG_MS;
+ }
+}
+
+static void mp_event_common(int data, uint32_t events, struct polling_params *poll_params) {
int ret;
unsigned long long evcount;
int64_t mem_usage, memsw_usage;
int64_t mem_pressure;
enum vmpressure_level lvl;
union meminfo mi;
- union zoneinfo zi;
+ struct zoneinfo zi;
struct timespec curr_tm;
static struct timespec last_kill_tm;
static unsigned long kill_skip_count = 0;
@@ -1657,6 +2130,15 @@
}
}
+ /* Start polling after initial PSI event */
+ if (use_psi_monitors && events) {
+ /* Override polling params only if current event is more critical */
+ if (!poll_params->poll_handler || data > poll_params->poll_handler->data) {
+ poll_params->polling_interval_ms = PSI_POLL_PERIOD_SHORT_MS;
+ poll_params->update = POLLING_START;
+ }
+ }
+
if (clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm) != 0) {
ALOGE("Failed to get current time");
return;
@@ -1687,7 +2169,7 @@
if (use_minfree_levels) {
int i;
- other_free = mi.field.nr_free_pages - zi.field.totalreserve_pages;
+ other_free = mi.field.nr_free_pages - zi.totalreserve_pages;
if (mi.field.nr_file_pages > (mi.field.shmem + mi.field.unevictable + mi.field.swap_cached)) {
other_file = (mi.field.nr_file_pages - mi.field.shmem -
mi.field.unevictable - mi.field.swap_cached);
@@ -1769,7 +2251,7 @@
do_kill:
if (low_ram_device) {
/* For Go devices kill only one task */
- if (find_and_kill_process(level_oomadj[level]) == 0) {
+ if (find_and_kill_process(level_oomadj[level], NULL) == 0) {
if (debug_process_killing) {
ALOGI("Nothing to kill");
}
@@ -1794,7 +2276,7 @@
min_score_adj = level_oomadj[level];
}
- pages_freed = find_and_kill_process(min_score_adj);
+ pages_freed = find_and_kill_process(min_score_adj, NULL);
if (pages_freed == 0) {
/* Rate limit kill reports when nothing was reclaimed */
@@ -1815,7 +2297,7 @@
"free(%" PRId64 "kB)-reserved(%" PRId64 "kB) below min(%ldkB) for oom_adj %d",
pages_freed * page_k,
other_file * page_k, mi.field.nr_free_pages * page_k,
- zi.field.totalreserve_pages * page_k,
+ zi.totalreserve_pages * page_k,
minfree * page_k, min_score_adj);
} else {
ALOGI("Reclaimed %ldkB at oom_adj %d",
@@ -1831,8 +2313,15 @@
}
}
-static bool init_mp_psi(enum vmpressure_level level) {
- int fd = init_psi_monitor(psi_thresholds[level].stall_type,
+static bool init_mp_psi(enum vmpressure_level level, bool use_new_strategy) {
+ int fd;
+
+ /* Do not register a handler if threshold_ms is not set */
+ if (!psi_thresholds[level].threshold_ms) {
+ return true;
+ }
+
+ fd = init_psi_monitor(psi_thresholds[level].stall_type,
psi_thresholds[level].threshold_ms * US_PER_MS,
PSI_WINDOW_SIZE_MS * US_PER_MS);
@@ -1840,7 +2329,7 @@
return false;
}
- vmpressure_hinfo[level].handler = mp_event_common;
+ vmpressure_hinfo[level].handler = use_new_strategy ? mp_event_psi : mp_event_common;
vmpressure_hinfo[level].data = level;
if (register_psi_monitor(epollfd, fd, &vmpressure_hinfo[level]) < 0) {
destroy_psi_monitor(fd);
@@ -1864,14 +2353,29 @@
}
static bool init_psi_monitors() {
- if (!init_mp_psi(VMPRESS_LEVEL_LOW)) {
+ /*
+ * When PSI is used on low-ram devices or on high-end devices without memfree levels
+ * use new kill strategy based on zone watermarks, free swap and thrashing stats
+ */
+ bool use_new_strategy =
+ property_get_bool("ro.lmk.use_new_strategy", low_ram_device || !use_minfree_levels);
+
+ /* In default PSI mode override stall amounts using system properties */
+ if (use_new_strategy) {
+ /* Do not use low pressure level */
+ psi_thresholds[VMPRESS_LEVEL_LOW].threshold_ms = 0;
+ psi_thresholds[VMPRESS_LEVEL_MEDIUM].threshold_ms = psi_partial_stall_ms;
+ psi_thresholds[VMPRESS_LEVEL_CRITICAL].threshold_ms = psi_complete_stall_ms;
+ }
+
+ if (!init_mp_psi(VMPRESS_LEVEL_LOW, use_new_strategy)) {
return false;
}
- if (!init_mp_psi(VMPRESS_LEVEL_MEDIUM)) {
+ if (!init_mp_psi(VMPRESS_LEVEL_MEDIUM, use_new_strategy)) {
destroy_mp_psi(VMPRESS_LEVEL_LOW);
return false;
}
- if (!init_mp_psi(VMPRESS_LEVEL_CRITICAL)) {
+ if (!init_mp_psi(VMPRESS_LEVEL_CRITICAL, use_new_strategy)) {
destroy_mp_psi(VMPRESS_LEVEL_MEDIUM);
destroy_mp_psi(VMPRESS_LEVEL_LOW);
return false;
@@ -1946,74 +2450,17 @@
return false;
}
-#ifdef LMKD_LOG_STATS
-static int kernel_poll_fd = -1;
-static void poll_kernel() {
- if (kernel_poll_fd == -1) {
- // not waiting
- return;
- }
-
- while (1) {
- char rd_buf[256];
- int bytes_read =
- TEMP_FAILURE_RETRY(pread(kernel_poll_fd, (void*)rd_buf, sizeof(rd_buf), 0));
- if (bytes_read <= 0) break;
- rd_buf[bytes_read] = '\0';
-
- int64_t pid;
- int64_t uid;
- int64_t group_leader_pid;
- int64_t min_flt;
- int64_t maj_flt;
- int64_t rss_in_pages;
- int16_t oom_score_adj;
- int16_t min_score_adj;
- int64_t starttime;
- char* taskname = 0;
- int fields_read = sscanf(rd_buf,
- "%" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64
- " %" SCNd64 " %" SCNd16 " %" SCNd16 " %" SCNd64 "\n%m[^\n]",
- &pid, &uid, &group_leader_pid, &min_flt, &maj_flt, &rss_in_pages,
- &oom_score_adj, &min_score_adj, &starttime, &taskname);
-
- /* only the death of the group leader process is logged */
- if (fields_read == 10 && group_leader_pid == pid) {
- int64_t process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
- stats_write_lmk_kill_occurred_pid(log_ctx, LMK_KILL_OCCURRED, uid, pid, oom_score_adj,
- min_flt, maj_flt, rss_in_pages * PAGE_SIZE, 0, 0,
- process_start_time_ns, min_score_adj);
- }
-
- free(taskname);
- }
+static void kernel_event_handler(int data __unused, uint32_t events __unused,
+ struct polling_params *poll_params __unused) {
+ kpoll_info.handler(kpoll_info.poll_fd);
}
-static struct event_handler_info kernel_poll_hinfo = {0, poll_kernel};
-
-static void init_poll_kernel() {
- struct epoll_event epev;
- kernel_poll_fd =
- TEMP_FAILURE_RETRY(open("/proc/lowmemorykiller", O_RDONLY | O_NONBLOCK | O_CLOEXEC));
-
- if (kernel_poll_fd < 0) {
- ALOGE("kernel lmk event file could not be opened; errno=%d", kernel_poll_fd);
- return;
- }
-
- epev.events = EPOLLIN;
- epev.data.ptr = (void*)&kernel_poll_hinfo;
- if (epoll_ctl(epollfd, EPOLL_CTL_ADD, kernel_poll_fd, &epev) != 0) {
- ALOGE("epoll_ctl for lmk events failed; errno=%d", errno);
- close(kernel_poll_fd);
- kernel_poll_fd = -1;
- } else {
- maxevents++;
- }
-}
-#endif
-
static int init(void) {
+ static struct event_handler_info kernel_poll_hinfo = { 0, kernel_event_handler };
+ struct reread_data file_data = {
+ .filename = ZONEINFO_PATH,
+ .fd = -1,
+ };
struct epoll_event epev;
int i;
int ret;
@@ -2060,11 +2507,17 @@
if (use_inkernel_interface) {
ALOGI("Using in-kernel low memory killer interface");
-#ifdef LMKD_LOG_STATS
- if (enable_stats_log) {
- init_poll_kernel();
+ if (init_poll_kernel(&kpoll_info)) {
+ epev.events = EPOLLIN;
+ epev.data.ptr = (void*)&kernel_poll_hinfo;
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, kpoll_info.poll_fd, &epev) != 0) {
+ ALOGE("epoll_ctl for lmk events failed (errno=%d)", errno);
+ close(kpoll_info.poll_fd);
+ kpoll_info.poll_fd = -1;
+ } else {
+ maxevents++;
+ }
}
-#endif
} else {
/* Try to use psi monitor first if kernel has it */
use_psi_monitors = property_get_bool("ro.lmk.use_psi", true) &&
@@ -2091,37 +2544,68 @@
memset(killcnt_idx, KILLCNT_INVALID_IDX, sizeof(killcnt_idx));
+ /*
+ * Read zoneinfo as the biggest file we read to create and size the initial
+ * read buffer and avoid memory re-allocations during memory pressure
+ */
+ if (reread_file(&file_data) == NULL) {
+ ALOGE("Failed to read %s: %s", file_data.filename, strerror(errno));
+ }
+
return 0;
}
static void mainloop(void) {
struct event_handler_info* handler_info;
- struct event_handler_info* poll_handler = NULL;
- struct timespec last_report_tm, curr_tm;
+ struct polling_params poll_params;
+ struct timespec curr_tm;
struct epoll_event *evt;
long delay = -1;
- int polling = 0;
+
+ poll_params.poll_handler = NULL;
+ poll_params.update = POLLING_DO_NOT_CHANGE;
while (1) {
struct epoll_event events[maxevents];
int nevents;
int i;
- if (polling) {
+ if (poll_params.poll_handler) {
/* Calculate next timeout */
clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm);
- delay = get_time_diff_ms(&last_report_tm, &curr_tm);
- delay = (delay < PSI_POLL_PERIOD_MS) ?
- PSI_POLL_PERIOD_MS - delay : PSI_POLL_PERIOD_MS;
+ delay = get_time_diff_ms(&poll_params.last_poll_tm, &curr_tm);
+ delay = (delay < poll_params.polling_interval_ms) ?
+ poll_params.polling_interval_ms - delay : poll_params.polling_interval_ms;
/* Wait for events until the next polling timeout */
nevents = epoll_wait(epollfd, events, maxevents, delay);
clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm);
- if (get_time_diff_ms(&last_report_tm, &curr_tm) >= PSI_POLL_PERIOD_MS) {
- polling--;
- poll_handler->handler(poll_handler->data, 0);
- last_report_tm = curr_tm;
+ if (get_time_diff_ms(&poll_params.last_poll_tm, &curr_tm) >=
+ poll_params.polling_interval_ms) {
+ /* Set input params for the call */
+ poll_params.poll_handler->handler(poll_params.poll_handler->data, 0, &poll_params);
+ poll_params.last_poll_tm = curr_tm;
+
+ if (poll_params.update != POLLING_DO_NOT_CHANGE) {
+ switch (poll_params.update) {
+ case POLLING_START:
+ poll_params.poll_start_tm = curr_tm;
+ break;
+ case POLLING_STOP:
+ poll_params.poll_handler = NULL;
+ break;
+ default:
+ break;
+ }
+ poll_params.update = POLLING_DO_NOT_CHANGE;
+ } else {
+ if (get_time_diff_ms(&poll_params.poll_start_tm, &curr_tm) >
+ PSI_WINDOW_SIZE_MS) {
+ /* Polled for the duration of PSI window, time to stop */
+ poll_params.poll_handler = NULL;
+ }
+ }
}
} else {
/* Wait for events with no timeout */
@@ -2152,25 +2636,37 @@
/* Second pass to handle all other events */
for (i = 0, evt = &events[0]; i < nevents; ++i, evt++) {
- if (evt->events & EPOLLERR)
+ if (evt->events & EPOLLERR) {
ALOGD("EPOLLERR on event #%d", i);
+ }
if (evt->events & EPOLLHUP) {
/* This case was handled in the first pass */
continue;
}
if (evt->data.ptr) {
handler_info = (struct event_handler_info*)evt->data.ptr;
- handler_info->handler(handler_info->data, evt->events);
+ /* Set input params for the call */
+ handler_info->handler(handler_info->data, evt->events, &poll_params);
- if (use_psi_monitors && handler_info->handler == mp_event_common) {
- /*
- * Poll for the duration of PSI_WINDOW_SIZE_MS after the
- * initial PSI event because psi events are rate-limited
- * at one per sec.
- */
- polling = PSI_POLL_COUNT;
- poll_handler = handler_info;
- clock_gettime(CLOCK_MONOTONIC_COARSE, &last_report_tm);
+ if (poll_params.update != POLLING_DO_NOT_CHANGE) {
+ switch (poll_params.update) {
+ case POLLING_START:
+ /*
+ * Poll for the duration of PSI_WINDOW_SIZE_MS after the
+ * initial PSI event because psi events are rate-limited
+ * at one per sec.
+ */
+ clock_gettime(CLOCK_MONOTONIC_COARSE, &curr_tm);
+ poll_params.poll_start_tm = poll_params.last_poll_tm = curr_tm;
+ poll_params.poll_handler = handler_info;
+ break;
+ case POLLING_STOP:
+ poll_params.poll_handler = NULL;
+ break;
+ default:
+ break;
+ }
+ poll_params.update = POLLING_DO_NOT_CHANGE;
}
}
}
@@ -2207,14 +2703,20 @@
property_get_bool("ro.lmk.use_minfree_levels", false);
per_app_memcg =
property_get_bool("ro.config.per_app_memcg", low_ram_device);
- swap_free_low_percentage =
- property_get_int32("ro.lmk.swap_free_low_percentage", 10);
+ swap_free_low_percentage = clamp(0, 100, property_get_int32("ro.lmk.swap_free_low_percentage",
+ low_ram_device ? DEF_LOW_SWAP_LOWRAM : DEF_LOW_SWAP));
+ psi_partial_stall_ms = property_get_int32("ro.lmk.psi_partial_stall_ms",
+ low_ram_device ? DEF_PARTIAL_STALL_LOWRAM : DEF_PARTIAL_STALL);
+ psi_complete_stall_ms = property_get_int32("ro.lmk.psi_complete_stall_ms",
+ DEF_COMPLETE_STALL);
+ thrashing_limit_pct = max(0, property_get_int32("ro.lmk.thrashing_limit",
+ low_ram_device ? DEF_THRASHING_LOWRAM : DEF_THRASHING));
+ thrashing_limit_decay_pct = clamp(0, 100, property_get_int32("ro.lmk.thrashing_limit_decay",
+ low_ram_device ? DEF_THRASHING_DECAY_LOWRAM : DEF_THRASHING_DECAY));
ctx = create_android_logger(MEMINFO_LOG_TAG);
-#ifdef LMKD_LOG_STATS
- statslog_init(&log_ctx, &enable_stats_log);
-#endif
+ statslog_init();
if (!init()) {
if (!use_inkernel_interface) {
@@ -2243,9 +2745,7 @@
mainloop();
}
-#ifdef LMKD_LOG_STATS
- statslog_destroy(&log_ctx);
-#endif
+ statslog_destroy();
android_log_destroy(&ctx);
diff --git a/lmkd/statslog.c b/lmkd/statslog.c
index f3a6e55..c0fd6df 100644
--- a/lmkd/statslog.c
+++ b/lmkd/statslog.c
@@ -18,11 +18,21 @@
#include <errno.h>
#include <log/log_id.h>
#include <stats_event_list.h>
+#include <statslog.h>
+#include <stdlib.h>
+#include <string.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
+#ifdef LMKD_LOG_STATS
+
#define LINE_MAX 128
+#define STRINGIFY(x) STRINGIFY_INTERNAL(x)
+#define STRINGIFY_INTERNAL(x) #x
+
+static bool enable_stats_log;
+static android_log_context log_ctx;
struct proc {
int pid;
@@ -41,34 +51,53 @@
return (int64_t)t.tv_sec * 1000000000LL + t.tv_nsec;
}
+void statslog_init() {
+ enable_stats_log = property_get_bool("ro.lmk.log_stats", false);
+
+ if (enable_stats_log) {
+ log_ctx = create_android_logger(kStatsEventTag);
+ }
+}
+
+void statslog_destroy() {
+ if (log_ctx) {
+ android_log_destroy(&log_ctx);
+ }
+}
+
/**
* Logs the change in LMKD state which is used as start/stop boundaries for logging
* LMK_KILL_OCCURRED event.
* Code: LMK_STATE_CHANGED = 54
*/
int
-stats_write_lmk_state_changed(android_log_context ctx, int32_t code, int32_t state) {
- assert(ctx != NULL);
+stats_write_lmk_state_changed(int32_t code, int32_t state) {
int ret = -EINVAL;
- if (!ctx) {
+
+ if (!enable_stats_log) {
return ret;
}
- reset_log_context(ctx);
-
- if ((ret = android_log_write_int64(ctx, getElapsedRealTimeNs())) < 0) {
+ assert(log_ctx != NULL);
+ if (!log_ctx) {
return ret;
}
- if ((ret = android_log_write_int32(ctx, code)) < 0) {
+ reset_log_context(log_ctx);
+
+ if ((ret = android_log_write_int64(log_ctx, getElapsedRealTimeNs())) < 0) {
return ret;
}
- if ((ret = android_log_write_int32(ctx, state)) < 0) {
+ if ((ret = android_log_write_int32(log_ctx, code)) < 0) {
return ret;
}
- return write_to_logger(ctx, LOG_ID_STATS);
+ if ((ret = android_log_write_int32(log_ctx, state)) < 0) {
+ return ret;
+ }
+
+ return write_to_logger(log_ctx, LOG_ID_STATS);
}
static struct proc* pid_lookup(int pid) {
@@ -87,92 +116,261 @@
* Code: LMK_KILL_OCCURRED = 51
*/
int
-stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
- char const* process_name, int32_t oom_score, int64_t pgfault,
- int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
- int64_t swap_in_bytes, int64_t process_start_time_ns,
- int32_t min_oom_score) {
- assert(ctx != NULL);
+stats_write_lmk_kill_occurred(int32_t code, int32_t uid, char const* process_name,
+ int32_t oom_score, int32_t min_oom_score, int tasksize,
+ struct memory_stat *mem_st) {
int ret = -EINVAL;
- if (!ctx) {
+ if (!enable_stats_log) {
return ret;
}
- reset_log_context(ctx);
+ if (!log_ctx) {
+ return ret;
+ }
+ reset_log_context(log_ctx);
- if ((ret = android_log_write_int64(ctx, getElapsedRealTimeNs())) < 0) {
+ if ((ret = android_log_write_int64(log_ctx, getElapsedRealTimeNs())) < 0) {
return ret;
}
- if ((ret = android_log_write_int32(ctx, code)) < 0) {
+ if ((ret = android_log_write_int32(log_ctx, code)) < 0) {
return ret;
}
- if ((ret = android_log_write_int32(ctx, uid)) < 0) {
+ if ((ret = android_log_write_int32(log_ctx, uid)) < 0) {
return ret;
}
- if ((ret = android_log_write_string8(ctx, (process_name == NULL) ? "" : process_name)) < 0) {
+ if ((ret = android_log_write_string8(log_ctx, (process_name == NULL) ? "" : process_name)) < 0) {
return ret;
}
- if ((ret = android_log_write_int32(ctx, oom_score)) < 0) {
+ if ((ret = android_log_write_int32(log_ctx, oom_score)) < 0) {
return ret;
}
- if ((ret = android_log_write_int64(ctx, pgfault)) < 0) {
+ if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->pgfault : -1)) < 0) {
return ret;
}
- if ((ret = android_log_write_int64(ctx, pgmajfault)) < 0) {
+ if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->pgmajfault : -1)) < 0) {
return ret;
}
- if ((ret = android_log_write_int64(ctx, rss_in_bytes)) < 0) {
+ if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->rss_in_bytes
+ : tasksize * BYTES_IN_KILOBYTE)) < 0) {
return ret;
}
- if ((ret = android_log_write_int64(ctx, cache_in_bytes)) < 0) {
+ if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->cache_in_bytes : -1)) < 0) {
return ret;
}
- if ((ret = android_log_write_int64(ctx, swap_in_bytes)) < 0) {
+ if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->swap_in_bytes : -1)) < 0) {
return ret;
}
- if ((ret = android_log_write_int64(ctx, process_start_time_ns)) < 0) {
+ if ((ret = android_log_write_int64(log_ctx, mem_st ? mem_st->process_start_time_ns
+ : -1)) < 0) {
return ret;
}
- if ((ret = android_log_write_int32(ctx, min_oom_score)) < 0) {
+ if ((ret = android_log_write_int32(log_ctx, min_oom_score)) < 0) {
return ret;
}
- return write_to_logger(ctx, LOG_ID_STATS);
+ return write_to_logger(log_ctx, LOG_ID_STATS);
}
-int stats_write_lmk_kill_occurred_pid(android_log_context ctx, int32_t code, int32_t uid, int pid,
- int32_t oom_score, int64_t pgfault, int64_t pgmajfault,
- int64_t rss_in_bytes, int64_t cache_in_bytes,
- int64_t swap_in_bytes, int64_t process_start_time_ns,
- int32_t min_oom_score) {
+static int stats_write_lmk_kill_occurred_pid(int32_t code, int32_t uid, int pid,
+ int32_t oom_score, int32_t min_oom_score, int tasksize,
+ struct memory_stat *mem_st) {
struct proc* proc = pid_lookup(pid);
if (!proc) return -EINVAL;
- return stats_write_lmk_kill_occurred(ctx, code, uid, proc->taskname, oom_score, pgfault,
- pgmajfault, rss_in_bytes, cache_in_bytes, swap_in_bytes,
- process_start_time_ns, min_oom_score);
+ return stats_write_lmk_kill_occurred(code, uid, proc->taskname, oom_score, min_oom_score,
+ tasksize, mem_st);
+}
+
+static void memory_stat_parse_line(char* line, struct memory_stat* mem_st) {
+ char key[LINE_MAX + 1];
+ int64_t value;
+
+ sscanf(line, "%" STRINGIFY(LINE_MAX) "s %" SCNd64 "", key, &value);
+
+ if (strcmp(key, "total_") < 0) {
+ return;
+ }
+
+ if (!strcmp(key, "total_pgfault"))
+ mem_st->pgfault = value;
+ else if (!strcmp(key, "total_pgmajfault"))
+ mem_st->pgmajfault = value;
+ else if (!strcmp(key, "total_rss"))
+ mem_st->rss_in_bytes = value;
+ else if (!strcmp(key, "total_cache"))
+ mem_st->cache_in_bytes = value;
+ else if (!strcmp(key, "total_swap"))
+ mem_st->swap_in_bytes = value;
+}
+
+static int memory_stat_from_cgroup(struct memory_stat* mem_st, int pid, uid_t uid) {
+ FILE *fp;
+ char buf[PATH_MAX];
+
+ snprintf(buf, sizeof(buf), MEMCG_PROCESS_MEMORY_STAT_PATH, uid, pid);
+
+ fp = fopen(buf, "r");
+
+ if (fp == NULL) {
+ return -1;
+ }
+
+ while (fgets(buf, PAGE_SIZE, fp) != NULL) {
+ memory_stat_parse_line(buf, mem_st);
+ }
+ fclose(fp);
+
+ return 0;
+}
+
+static int memory_stat_from_procfs(struct memory_stat* mem_st, int pid) {
+ char path[PATH_MAX];
+ char buffer[PROC_STAT_BUFFER_SIZE];
+ int fd, ret;
+
+ snprintf(path, sizeof(path), PROC_STAT_FILE_PATH, pid);
+ if ((fd = open(path, O_RDONLY | O_CLOEXEC)) < 0) {
+ return -1;
+ }
+
+ ret = read(fd, buffer, sizeof(buffer));
+ if (ret < 0) {
+ close(fd);
+ return -1;
+ }
+ close(fd);
+
+ // field 10 is pgfault
+ // field 12 is pgmajfault
+ // field 22 is starttime
+ // field 24 is rss_in_pages
+ int64_t pgfault = 0, pgmajfault = 0, starttime = 0, rss_in_pages = 0;
+ if (sscanf(buffer,
+ "%*u %*s %*s %*d %*d %*d %*d %*d %*d %" SCNd64 " %*d "
+ "%" SCNd64 " %*d %*u %*u %*d %*d %*d %*d %*d %*d "
+ "%" SCNd64 " %*d %" SCNd64 "",
+ &pgfault, &pgmajfault, &starttime, &rss_in_pages) != 4) {
+ return -1;
+ }
+ mem_st->pgfault = pgfault;
+ mem_st->pgmajfault = pgmajfault;
+ mem_st->rss_in_bytes = (rss_in_pages * PAGE_SIZE);
+ mem_st->process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
+
+ return 0;
+}
+
+struct memory_stat *stats_read_memory_stat(bool per_app_memcg, int pid, uid_t uid) {
+ static struct memory_stat mem_st = {};
+
+ if (!enable_stats_log) {
+ return NULL;
+ }
+
+ if (per_app_memcg) {
+ if (memory_stat_from_cgroup(&mem_st, pid, uid) == 0) {
+ return &mem_st;
+ }
+ } else {
+ if (memory_stat_from_procfs(&mem_st, pid) == 0) {
+ return &mem_st;
+ }
+ }
+
+ return NULL;
+}
+
+static void poll_kernel(int poll_fd) {
+ if (poll_fd == -1) {
+ // not waiting
+ return;
+ }
+
+ while (1) {
+ char rd_buf[256];
+ int bytes_read =
+ TEMP_FAILURE_RETRY(pread(poll_fd, (void*)rd_buf, sizeof(rd_buf), 0));
+ if (bytes_read <= 0) break;
+ rd_buf[bytes_read] = '\0';
+
+ int64_t pid;
+ int64_t uid;
+ int64_t group_leader_pid;
+ int64_t rss_in_pages;
+ struct memory_stat mem_st = {};
+ int16_t oom_score_adj;
+ int16_t min_score_adj;
+ int64_t starttime;
+ char* taskname = 0;
+
+ int fields_read = sscanf(rd_buf,
+ "%" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64 " %" SCNd64
+ " %" SCNd64 " %" SCNd16 " %" SCNd16 " %" SCNd64 "\n%m[^\n]",
+ &pid, &uid, &group_leader_pid, &mem_st.pgfault,
+ &mem_st.pgmajfault, &rss_in_pages, &oom_score_adj,
+ &min_score_adj, &starttime, &taskname);
+
+ /* only the death of the group leader process is logged */
+ if (fields_read == 10 && group_leader_pid == pid) {
+ mem_st.process_start_time_ns = starttime * (NS_PER_SEC / sysconf(_SC_CLK_TCK));
+ mem_st.rss_in_bytes = rss_in_pages * PAGE_SIZE;
+ stats_write_lmk_kill_occurred_pid(LMK_KILL_OCCURRED, uid, pid, oom_score_adj,
+ min_score_adj, 0, &mem_st);
+ }
+
+ free(taskname);
+ }
+}
+
+bool init_poll_kernel(struct kernel_poll_info *poll_info) {
+ if (!enable_stats_log) {
+ return false;
+ }
+
+ poll_info->poll_fd =
+ TEMP_FAILURE_RETRY(open("/proc/lowmemorykiller", O_RDONLY | O_NONBLOCK | O_CLOEXEC));
+
+ if (poll_info->poll_fd < 0) {
+ ALOGE("kernel lmk event file could not be opened; errno=%d", errno);
+ return false;
+ }
+ poll_info->handler = poll_kernel;
+
+ return true;
}
static void proc_insert(struct proc* procp) {
- if (!pidhash)
+ if (!pidhash) {
pidhash = calloc(PIDHASH_SZ, sizeof(struct proc));
+ }
+
int hval = pid_hashfn(procp->pid);
procp->pidhash_next = pidhash[hval];
pidhash[hval] = procp;
}
-void stats_remove_taskname(int pid) {
- if (!pidhash) return;
+void stats_remove_taskname(int pid, int poll_fd) {
+ if (!enable_stats_log || !pidhash) {
+ return;
+ }
+
+ /*
+ * Perform an extra check before the pid is removed, after which it
+ * will be impossible for poll_kernel to get the taskname. poll_kernel()
+ * is potentially a long-running blocking function; however this method
+ * handles AMS requests but does not block AMS.
+ */
+ poll_kernel(poll_fd);
int hval = pid_hashfn(pid);
struct proc* procp;
@@ -193,12 +391,19 @@
free(procp);
}
-void stats_store_taskname(int pid, const char* taskname) {
- struct proc* procp = pid_lookup(pid);
- if (procp != NULL && strcmp(procp->taskname, taskname) == 0)
+void stats_store_taskname(int pid, const char* taskname, int poll_fd) {
+ if (!enable_stats_log) {
return;
+ }
+
+ struct proc* procp = pid_lookup(pid);
+ if (procp != NULL) {
+ if (strcmp(procp->taskname, taskname) == 0) {
+ return;
+ }
+ stats_remove_taskname(pid, poll_fd);
+ }
procp = malloc(sizeof(struct proc));
- stats_remove_taskname(pid);
procp->pid = pid;
strncpy(procp->taskname, taskname, LINE_MAX - 1);
procp->taskname[LINE_MAX - 1] = '\0';
@@ -206,7 +411,10 @@
}
void stats_purge_tasknames() {
- if (!pidhash) return;
+ if (!enable_stats_log || !pidhash) {
+ return;
+ }
+
struct proc* procp;
struct proc* next;
int i;
@@ -220,3 +428,5 @@
}
memset(pidhash, 0, PIDHASH_SZ * sizeof(struct proc));
}
+
+#endif /* LMKD_LOG_STATS */
diff --git a/lmkd/statslog.h b/lmkd/statslog.h
index 50d69f7..a09c7dd 100644
--- a/lmkd/statslog.h
+++ b/lmkd/statslog.h
@@ -18,6 +18,7 @@
#define _STATSLOG_H_
#include <assert.h>
+#include <inttypes.h>
#include <stats_event_list.h>
#include <stdbool.h>
#include <sys/cdefs.h>
@@ -26,6 +27,20 @@
__BEGIN_DECLS
+struct memory_stat {
+ int64_t pgfault;
+ int64_t pgmajfault;
+ int64_t rss_in_bytes;
+ int64_t cache_in_bytes;
+ int64_t swap_in_bytes;
+ int64_t process_start_time_ns;
+};
+
+struct kernel_poll_info {
+ int poll_fd;
+ void (*handler)(int poll_fd);
+};
+
/*
* These are defined in
* http://cs/android/frameworks/base/cmds/statsd/src/atoms.proto
@@ -35,37 +50,17 @@
#define LMK_STATE_CHANGE_START 1
#define LMK_STATE_CHANGE_STOP 2
+#ifdef LMKD_LOG_STATS
+
/*
* The single event tag id for all stats logs.
* Keep this in sync with system/core/logcat/event.logtags
*/
const static int kStatsEventTag = 1937006964;
-static inline void statslog_init(android_log_context* log_ctx, bool* enable_stats_log) {
- assert(log_ctx != NULL);
- assert(enable_stats_log != NULL);
- *enable_stats_log = property_get_bool("ro.lmk.log_stats", false);
+void statslog_init();
- if (*enable_stats_log) {
- *log_ctx = create_android_logger(kStatsEventTag);
- }
-}
-
-static inline void statslog_destroy(android_log_context* log_ctx) {
- assert(log_ctx != NULL);
- if (*log_ctx) {
- android_log_destroy(log_ctx);
- }
-}
-
-struct memory_stat {
- int64_t pgfault;
- int64_t pgmajfault;
- int64_t rss_in_bytes;
- int64_t cache_in_bytes;
- int64_t swap_in_bytes;
- int64_t process_start_time_ns;
-};
+void statslog_destroy();
#define MEMCG_PROCESS_MEMORY_STAT_PATH "/dev/memcg/apps/uid_%u/pid_%u/memory.stat"
#define PROC_STAT_FILE_PATH "/proc/%d/stat"
@@ -78,47 +73,63 @@
* Code: LMK_STATE_CHANGED = 54
*/
int
-stats_write_lmk_state_changed(android_log_context ctx, int32_t code, int32_t state);
+stats_write_lmk_state_changed(int32_t code, int32_t state);
/**
* Logs the event when LMKD kills a process to reduce memory pressure.
* Code: LMK_KILL_OCCURRED = 51
*/
int
-stats_write_lmk_kill_occurred_pid(android_log_context ctx, int32_t code, int32_t uid, int pid,
- int32_t oom_score, int64_t pgfault, int64_t pgmajfault,
- int64_t rss_in_bytes, int64_t cache_in_bytes,
- int64_t swap_in_bytes, int64_t process_start_time_ns,
- int32_t min_oom_score);
+stats_write_lmk_kill_occurred(int32_t code, int32_t uid,
+ char const* process_name, int32_t oom_score, int32_t min_oom_score,
+ int tasksize, struct memory_stat *mem_st);
-/**
- * Logs the event when LMKD kills a process to reduce memory pressure.
- * Code: LMK_KILL_OCCURRED = 51
- */
-int
-stats_write_lmk_kill_occurred(android_log_context ctx, int32_t code, int32_t uid,
- char const* process_name, int32_t oom_score, int64_t pgfault,
- int64_t pgmajfault, int64_t rss_in_bytes, int64_t cache_in_bytes,
- int64_t swap_in_bytes, int64_t process_start_time_ns,
- int32_t min_oom_score);
+struct memory_stat *stats_read_memory_stat(bool per_app_memcg, int pid, uid_t uid);
/**
* Registers a process taskname by pid, while it is still alive.
*/
-void
-stats_store_taskname(int pid, const char* taskname);
+void stats_store_taskname(int pid, const char* taskname, int poll_fd);
/**
* Unregister all process tasknames.
*/
-void
-stats_purge_tasknames();
+void stats_purge_tasknames();
/**
* Unregister a process taskname, e.g. after it has been killed.
*/
-void
-stats_remove_taskname(int pid);
+void stats_remove_taskname(int pid, int poll_fd);
+
+bool init_poll_kernel(struct kernel_poll_info *poll_info);
+
+#else /* LMKD_LOG_STATS */
+
+static inline void statslog_init() {}
+static inline void statslog_destroy() {}
+
+static inline int
+stats_write_lmk_state_changed(int32_t code __unused, int32_t state __unused) { return -EINVAL; }
+
+static inline int
+stats_write_lmk_kill_occurred(int32_t code __unused, int32_t uid __unused,
+ char const* process_name __unused, int32_t oom_score __unused,
+ int32_t min_oom_score __unused, int tasksize __unused,
+ struct memory_stat *mem_st __unused) { return -EINVAL; }
+
+static inline struct memory_stat *stats_read_memory_stat(bool per_app_memcg __unused,
+ int pid __unused, uid_t uid __unused) { return NULL; }
+
+static inline void stats_store_taskname(int pid __unused, const char* taskname __unused,
+ int poll_fd __unused) {}
+
+static inline void stats_purge_tasknames() {}
+
+static inline void stats_remove_taskname(int pid __unused, int poll_fd __unused) {}
+
+static inline bool init_poll_kernel(struct kernel_poll_info *poll_info __unused) { return false; }
+
+#endif /* LMKD_LOG_STATS */
__END_DECLS
diff --git a/rootdir/init.rc b/rootdir/init.rc
index c61fd90..9183901 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -760,11 +760,6 @@
write /sys/fs/f2fs/${dev.mnt.blk.data}/cp_interval 200
# Permissions for System Server and daemons.
- chown radio system /sys/android_power/state
- chown radio system /sys/android_power/request_state
- chown radio system /sys/android_power/acquire_full_wake_lock
- chown radio system /sys/android_power/acquire_partial_wake_lock
- chown radio system /sys/android_power/release_wake_lock
chown system system /sys/power/autosleep
chown system system /sys/devices/system/cpu/cpufreq/interactive/timer_rate
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index bf3fb42..9adbcba 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -5,7 +5,6 @@
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
- onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index 1bab588..f6149c9 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -5,7 +5,6 @@
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
- onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 6fa210a..0e69b16 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -5,7 +5,6 @@
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
- onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 48461ec..3e80168 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -5,7 +5,6 @@
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
- onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver