summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--boot_control_copy/Android.bp1
-rw-r--r--boot_control_copy/bootinfo.cpp20
-rw-r--r--libfec/Android.bp46
-rw-r--r--libfec/avb_utils.cpp160
-rw-r--r--libfec/avb_utils.h30
-rw-r--r--libfec/avb_utils_stub.cpp27
-rw-r--r--libfec/fec_open.cpp18
-rw-r--r--libfec/fec_private.h10
-rw-r--r--libfec/test/Android.bp3
-rw-r--r--libfec/test/fec_unittest.cpp96
-rw-r--r--libfscrypt/fscrypt.cpp6
-rw-r--r--libfscrypt/include/fscrypt/fscrypt.h18
-rw-r--r--libfscrypt/tests/fscrypt_test.cpp24
-rw-r--r--libperfmgr/.clang-format11
-rw-r--r--libperfmgr/Android.bp77
-rw-r--r--libperfmgr/CPPLINT.cfg2
-rw-r--r--libperfmgr/FileNode.cc113
-rw-r--r--libperfmgr/HintManager.cc379
-rw-r--r--libperfmgr/Node.cc100
-rw-r--r--libperfmgr/NodeLooperThread.cc146
-rw-r--r--libperfmgr/OWNERS3
-rw-r--r--libperfmgr/PropertyNode.cc79
-rw-r--r--libperfmgr/RequestGroup.cc64
-rw-r--r--libperfmgr/config_schema.json126
-rw-r--r--libperfmgr/include/perfmgr/FileNode.h55
-rw-r--r--libperfmgr/include/perfmgr/HintManager.h92
-rw-r--r--libperfmgr/include/perfmgr/Node.h91
-rw-r--r--libperfmgr/include/perfmgr/NodeLooperThread.h102
-rw-r--r--libperfmgr/include/perfmgr/PropertyNode.h48
-rw-r--r--libperfmgr/include/perfmgr/RequestGroup.h65
-rw-r--r--libperfmgr/tests/FileNodeTest.cc231
-rw-r--r--libperfmgr/tests/HintManagerTest.cc528
-rw-r--r--libperfmgr/tests/NodeLooperThreadTest.cc191
-rw-r--r--libperfmgr/tests/PropertyNodeTest.cc219
-rw-r--r--libperfmgr/tests/RequestGroupTest.cc164
-rw-r--r--libperfmgr/tools/ConfigVerifier.cc178
-rw-r--r--simpleperf/Android.bp23
-rw-r--r--simpleperf/cmd_record_test.cpp9
-rw-r--r--simpleperf/cmd_stat.cpp569
-rw-r--r--simpleperf/cmd_stat_impl.h278
-rw-r--r--simpleperf/cmd_stat_test.cpp207
-rw-r--r--simpleperf/doc/executable_commands_reference.md16
-rw-r--r--simpleperf/environment.cpp12
-rw-r--r--tests/kernel.config/Android.mk2
-rw-r--r--verity/Android.bp1
-rw-r--r--verity/fec/Android.bp5
46 files changed, 1179 insertions, 3466 deletions
diff --git a/boot_control_copy/Android.bp b/boot_control_copy/Android.bp
index a6712087..6c9dbc24 100644
--- a/boot_control_copy/Android.bp
+++ b/boot_control_copy/Android.bp
@@ -20,7 +20,6 @@ cc_library_shared {
"libcutils",
],
static_libs: [
- "libbootloader_message",
"libfs_mgr",
],
}
diff --git a/boot_control_copy/bootinfo.cpp b/boot_control_copy/bootinfo.cpp
index 364c1985..55319178 100644
--- a/boot_control_copy/bootinfo.cpp
+++ b/boot_control_copy/bootinfo.cpp
@@ -28,7 +28,6 @@
#include <stdlib.h>
#include <string.h>
-#include <bootloader_message/bootloader_message.h>
#include <cutils/properties.h>
#include <fs_mgr.h>
@@ -106,11 +105,20 @@ int boot_info_open_partition(const char *name, uint64_t *out_size, int flags)
}
// As per struct bootloader_message_ab which is defined in
-// bootable/recovery/bootloader.h we can use the 32 bytes in the
-// bootctrl_suffix field provided that they start with the active slot
-// suffix terminated by NUL. It just so happens that BrilloBootInfo is
-// laid out this way.
-#define BOOTINFO_OFFSET offsetof(struct bootloader_message_ab, slot_suffix)
+// boot/1.1/default.
+// struct bootloader_message_ab {
+// struct bootloader_message message;
+// char slot_suffix[32];
+// char update_channel[128];
+//
+// // Round up the entire struct to 4096-byte.
+// char reserved[1888];
+// };
+//
+// We can use the 32 bytes in the bootctrl_suffix field provided that they
+// start with the active slot suffix terminated by NUL. It just so happens
+// that BrilloBootInfo is laid out this way.
+#define BOOTINFO_OFFSET 2048
bool boot_info_load(BrilloBootInfo *out_info)
{
diff --git a/libfec/Android.bp b/libfec/Android.bp
index 78f2fedd..3da6fe7c 100644
--- a/libfec/Android.bp
+++ b/libfec/Android.bp
@@ -1,16 +1,22 @@
// Copyright 2015 The Android Open Source Project
-cc_library {
- name: "libfec",
- host_supported: true,
- recovery_available: true,
+cc_defaults {
+ name: "libfec_default",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-O3",
+ "-D_LARGEFILE64_SOURCE",
+ ],
+
srcs: [
"fec_open.cpp",
"fec_read.cpp",
"fec_verity.cpp",
"fec_process.cpp",
],
- cflags: ["-Wall", "-Werror", "-O3", "-D_LARGEFILE64_SOURCE"],
+
export_include_dirs: ["include"],
// Exported header include/fec/io.h includes crypto_utils headers.
export_shared_lib_headers: ["libcrypto_utils"],
@@ -30,7 +36,10 @@ cc_library {
target: {
host: {
- cflags: ["-D_GNU_SOURCE", "-DFEC_NO_KLOG"]
+ cflags: [
+ "-D_GNU_SOURCE",
+ "-DFEC_NO_KLOG",
+ ],
},
linux_glibc: {
sanitize: {
@@ -39,3 +48,28 @@ cc_library {
},
},
}
+
+cc_library {
+ name: "libfec",
+ defaults: ["libfec_default"],
+ host_supported: true,
+ recovery_available: true,
+
+ target: {
+ linux: {
+ srcs: [
+ "avb_utils.cpp",
+ ],
+ static_libs: [
+ "libavb",
+ ],
+ },
+
+ // libavb isn't available on mac.
+ darwin: {
+ srcs: [
+ "avb_utils_stub.cpp",
+ ],
+ },
+ },
+}
diff --git a/libfec/avb_utils.cpp b/libfec/avb_utils.cpp
new file mode 100644
index 00000000..8913f2a1
--- /dev/null
+++ b/libfec/avb_utils.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2020 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 "avb_utils.h"
+
+#include <android-base/strings.h>
+#include <libavb/libavb.h>
+
+#include "fec_private.h"
+
+int parse_vbmeta_from_footer(fec_handle *f, std::vector<uint8_t> *vbmeta) {
+ if (f->size <= AVB_FOOTER_SIZE) {
+ debug("file size not large enough to be avb images:" PRIu64, f->size);
+ return -1;
+ }
+
+ AvbFooter footer_read;
+ if (!raw_pread(f->fd, &footer_read, AVB_FOOTER_SIZE,
+ f->size - AVB_FOOTER_SIZE)) {
+ error("failed to read footer: %s", strerror(errno));
+ return -1;
+ }
+
+ AvbFooter footer;
+ if (!avb_footer_validate_and_byteswap(&footer_read, &footer)) {
+ debug("invalid avb footer");
+ return -1;
+ }
+ uint64_t vbmeta_offset = footer.vbmeta_offset;
+ uint64_t vbmeta_size = footer.vbmeta_size;
+ check(vbmeta_offset <= f->size - sizeof(footer) - vbmeta_size);
+
+ std::vector<uint8_t> vbmeta_data(vbmeta_size, 0);
+ // TODO(xunchang) handle the sparse image with libsparse.
+ if (!raw_pread(f->fd, vbmeta_data.data(), vbmeta_data.size(),
+ vbmeta_offset)) {
+ error("failed to read avb vbmeta: %s", strerror(errno));
+ return -1;
+ }
+
+ if (auto status = avb_vbmeta_image_verify(
+ vbmeta_data.data(), vbmeta_data.size(), nullptr, nullptr);
+ status != AVB_VBMETA_VERIFY_RESULT_OK &&
+ status != AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED) {
+ error("failed to verify avb vbmeta, status: %d", status);
+ return -1;
+ }
+ *vbmeta = std::move(vbmeta_data);
+ return 0;
+}
+
+int parse_avb_image(fec_handle *f, const std::vector<uint8_t> &vbmeta) {
+ // TODO(xunchang) check if avb verification or hashtree is disabled.
+
+ // Look for the hashtree descriptor, we expect exactly one descriptor in
+ // vbmeta.
+ // TODO(xunchang) handle the image with AvbHashDescriptor.
+ auto parse_descriptor = [](const AvbDescriptor *descriptor,
+ void *user_data) {
+ if (descriptor &&
+ avb_be64toh(descriptor->tag) == AVB_DESCRIPTOR_TAG_HASHTREE) {
+ auto desp = static_cast<const AvbDescriptor **>(user_data);
+ *desp = descriptor;
+ return false;
+ }
+ return true;
+ };
+
+ const AvbHashtreeDescriptor *hashtree_descriptor_ptr = nullptr;
+ avb_descriptor_foreach(vbmeta.data(), vbmeta.size(), parse_descriptor,
+ &hashtree_descriptor_ptr);
+
+ AvbHashtreeDescriptor hashtree_descriptor;
+ if (!avb_hashtree_descriptor_validate_and_byteswap(hashtree_descriptor_ptr,
+ &hashtree_descriptor)) {
+ error("failed to verify avb hashtree descriptor");
+ return -1;
+ }
+
+ // The partition name, salt, root append right after the hashtree
+ // descriptor.
+ auto read_ptr = reinterpret_cast<const uint8_t *>(hashtree_descriptor_ptr);
+ // Calculate the offset with respect to the vbmeta; and check both the
+ // salt & root are within the range.
+ uint32_t salt_offset =
+ sizeof(AvbHashtreeDescriptor) + hashtree_descriptor.partition_name_len;
+ uint32_t root_offset = salt_offset + hashtree_descriptor.salt_len;
+ check(hashtree_descriptor.salt_len < vbmeta.size());
+ check(salt_offset < vbmeta.size() - hashtree_descriptor.salt_len);
+ check(hashtree_descriptor.root_digest_len < vbmeta.size());
+ check(root_offset < vbmeta.size() - hashtree_descriptor.root_digest_len);
+ std::vector<uint8_t> salt(
+ read_ptr + salt_offset,
+ read_ptr + salt_offset + hashtree_descriptor.salt_len);
+ std::vector<uint8_t> root_hash(
+ read_ptr + root_offset,
+ read_ptr + root_offset + hashtree_descriptor.root_digest_len);
+
+ // Expect the AVB image has the format:
+ // 1. hashtree
+ // 2. ecc data
+ // 3. vbmeta
+ // 4. avb footer
+ check(hashtree_descriptor.fec_offset ==
+ hashtree_descriptor.tree_offset + hashtree_descriptor.tree_size);
+ check(hashtree_descriptor.fec_offset <=
+ f->size - hashtree_descriptor.fec_size);
+
+ f->data_size = hashtree_descriptor.fec_offset;
+
+ f->ecc.blocks = fec_div_round_up(f->data_size, FEC_BLOCKSIZE);
+ f->ecc.rounds = fec_div_round_up(f->ecc.blocks, f->ecc.rsn);
+ f->ecc.size = hashtree_descriptor.fec_size;
+ f->ecc.start = hashtree_descriptor.fec_offset;
+ // TODO(xunchang) verify the integrity of the ecc data.
+ f->ecc.valid = true;
+
+ std::string hash_algorithm =
+ reinterpret_cast<char *>(hashtree_descriptor.hash_algorithm);
+ int nid = -1;
+ if (android::base::EqualsIgnoreCase(hash_algorithm, "sha1")) {
+ nid = NID_sha1;
+ } else if (android::base::EqualsIgnoreCase(hash_algorithm, "sha256")) {
+ nid = NID_sha256;
+ } else {
+ error("unsupported hash algorithm %s", hash_algorithm.c_str());
+ }
+
+ hashtree_info hashtree;
+ hashtree.initialize(hashtree_descriptor.tree_offset,
+ hashtree_descriptor.tree_offset / FEC_BLOCKSIZE, salt,
+ nid);
+ if (hashtree.verify_tree(f, root_hash.data()) != 0) {
+ error("failed to verify hashtree");
+ return -1;
+ }
+
+ // We have validate the hashtree,
+ f->data_size = hashtree.hash_start;
+ f->avb = {
+ .valid = true,
+ .vbmeta = vbmeta,
+ .hashtree = std::move(hashtree),
+ };
+
+ return 0;
+}
diff --git a/libfec/avb_utils.h b/libfec/avb_utils.h
new file mode 100644
index 00000000..78254508
--- /dev/null
+++ b/libfec/avb_utils.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <vector>
+
+struct fec_handle;
+
+// Checks if there is a valid AVB footer in the end of the image. If so, parses
+// the contents of vbmeta struct from the given AVB footer. Returns 0 on
+// success.
+int parse_vbmeta_from_footer(fec_handle *f, std::vector<uint8_t> *vbmeta);
+
+// Parses the AVB vbmeta for the information of hashtree and fec data.
+int parse_avb_image(fec_handle *f, const std::vector<uint8_t> &vbmeta);
diff --git a/libfec/avb_utils_stub.cpp b/libfec/avb_utils_stub.cpp
new file mode 100644
index 00000000..0b6061e5
--- /dev/null
+++ b/libfec/avb_utils_stub.cpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 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 "avb_utils.h"
+
+int parse_vbmeta_from_footer(fec_handle* /* f */,
+ std::vector<uint8_t> * /* vbmeta */) {
+ return -1;
+}
+
+int parse_avb_image(fec_handle* /* f */,
+ const std::vector<uint8_t>& /* vbmeta */) {
+ return -1;
+}
diff --git a/libfec/fec_open.cpp b/libfec/fec_open.cpp
index 18731f21..fe458097 100644
--- a/libfec/fec_open.cpp
+++ b/libfec/fec_open.cpp
@@ -29,6 +29,7 @@
#define fdatasync(fd) fcntl((fd), F_FULLFSYNC)
#endif
+#include "avb_utils.h"
#include "fec_private.h"
/* used by `find_offset'; returns metadata size for a file size `size' and
@@ -546,6 +547,23 @@ int fec_open(struct fec_handle **handle, const char *path, int mode, int flags,
f->data_size = f->size; /* until ecc and/or verity are loaded */
+ // Don't parse the avb image if FEC_NO_AVB is set. It's used when libavb is
+ // not supported on mac.
+ std::vector<uint8_t> vbmeta;
+ if (parse_vbmeta_from_footer(f.get(), &vbmeta) == 0) {
+ if (parse_avb_image(f.get(), vbmeta) != 0) {
+ error("failed to parse avb image.");
+ return -1;
+ }
+
+ *handle = f.release();
+ return 0;
+ }
+ // TODO(xunchang) For android, handle the case when vbmeta is in a separate
+ // image. We could use avb_slot_verify() && AvbOps from libavb_user.
+
+ // Fall back to use verity format.
+
if (load_ecc(f.get()) == -1) {
debug("error-correcting codes not found from '%s'", path);
}
diff --git a/libfec/fec_private.h b/libfec/fec_private.h
index 62687994..b28c4294 100644
--- a/libfec/fec_private.h
+++ b/libfec/fec_private.h
@@ -124,6 +124,12 @@ struct verity_info {
verity_header ecc_header;
};
+struct avb_info {
+ bool valid = false;
+ std::vector<uint8_t> vbmeta;
+ hashtree_info hashtree;
+};
+
struct fec_handle {
ecc_info ecc;
int fd;
@@ -134,10 +140,12 @@ struct fec_handle {
uint64_t data_size;
uint64_t pos;
uint64_t size;
+ // TODO(xunchang) switch to std::optional
verity_info verity;
+ avb_info avb;
hashtree_info hashtree() const {
- return verity.hashtree;
+ return avb.valid ? avb.hashtree : verity.hashtree;
}
};
diff --git a/libfec/test/Android.bp b/libfec/test/Android.bp
index 1bbad077..75182a27 100644
--- a/libfec/test/Android.bp
+++ b/libfec/test/Android.bp
@@ -24,6 +24,7 @@ cc_test_host {
static_libs: [
"libfec",
"libfec_rs",
+ "libavb",
"libcrypto_utils",
"libcrypto",
"libext4_utils",
@@ -47,12 +48,14 @@ cc_test_host {
gtest: true,
required: [
+ "avbtool",
"fec",
],
static_libs: [
"libverity_tree",
"libfec",
"libfec_rs",
+ "libavb",
"libcrypto_utils",
"libext4_utils",
"libsquashfs_utils",
diff --git a/libfec/test/fec_unittest.cpp b/libfec/test/fec_unittest.cpp
index ca5d91a8..421eb501 100644
--- a/libfec/test/fec_unittest.cpp
+++ b/libfec/test/fec_unittest.cpp
@@ -102,6 +102,20 @@ class FecUnitTest : public ::testing::Test {
ASSERT_EQ(0, std::system(android::base::Join(cmd, ' ').c_str()));
}
+ void AddAvbHashtreeFooter(const std::string &image_name,
+ std::string algorithm = "sha256") {
+ salt_ = std::vector<uint8_t>(64, 10);
+ std::vector<std::string> cmd = {
+ "avbtool", "add_hashtree_footer",
+ "--salt", HashTreeBuilder::BytesArrayToString(salt_),
+ "--hash_algorithm", algorithm,
+ "--image", image_name,
+ };
+ ASSERT_EQ(0, std::system(android::base::Join(cmd, ' ').c_str()));
+
+ BuildHashtree(algorithm);
+ }
+
std::vector<uint8_t> image_;
std::vector<uint8_t> salt_;
std::vector<uint8_t> root_hash_;
@@ -202,3 +216,85 @@ TEST_F(FecUnitTest, VerityImage_FecRead) {
ASSERT_EQ(1024, fec_pread(handle, read_data.data(), 1024, corrupt_offset));
ASSERT_EQ(std::vector<uint8_t>(1024, 255), read_data);
}
+
+TEST_F(FecUnitTest, LoadAvbImage_HashtreeFooter) {
+ TemporaryFile avb_image;
+ ASSERT_TRUE(
+ android::base::WriteFully(avb_image.fd, image_.data(), image_.size()));
+ AddAvbHashtreeFooter(avb_image.path);
+
+ struct fec_handle *handle = nullptr;
+ ASSERT_EQ(0, fec_open(&handle, avb_image.path, O_RDWR, FEC_FS_EXT4, 2));
+ std::unique_ptr<fec_handle> guard(handle);
+
+ ASSERT_EQ(1024 * 1024, handle->data_size); // filesystem size
+
+ ASSERT_TRUE(handle->avb.valid);
+
+ // check the hashtree.
+ ASSERT_EQ(salt_, handle->hashtree().salt);
+ ASSERT_EQ(1024 * 1024, handle->hashtree().hash_start);
+ // the fec hashtree only stores the hash of the lowest level.
+ ASSERT_EQ(std::vector<uint8_t>(hashtree_content_.begin() + 4096,
+ hashtree_content_.end()),
+ handle->hashtree().hash_data);
+ uint64_t hash_size =
+ verity_get_size(handle->hashtree().data_blocks * FEC_BLOCKSIZE, nullptr,
+ nullptr, SHA256_DIGEST_LENGTH);
+ ASSERT_EQ(hashtree_content_.size(), hash_size);
+
+ fec_ecc_metadata ecc_metadata{};
+ ASSERT_EQ(0, fec_ecc_get_metadata(handle, &ecc_metadata));
+ ASSERT_TRUE(ecc_metadata.valid);
+ ASSERT_EQ(1024 * 1024 + hash_size, ecc_metadata.start);
+ ASSERT_EQ(259, ecc_metadata.blocks);
+}
+
+TEST_F(FecUnitTest, LoadAvbImage_CorrectHashtree) {
+ TemporaryFile avb_image;
+ ASSERT_TRUE(
+ android::base::WriteFully(avb_image.fd, image_.data(), image_.size()));
+ AddAvbHashtreeFooter(avb_image.path);
+
+ uint64_t corrupt_offset = 1024 * 1024 + 2 * 4096 + 50;
+ ASSERT_EQ(corrupt_offset, lseek64(avb_image.fd, corrupt_offset, 0));
+ std::vector<uint8_t> corruption(20, 5);
+ ASSERT_TRUE(android::base::WriteFully(avb_image.fd, corruption.data(),
+ corruption.size()));
+
+ struct fec_handle *handle = nullptr;
+ ASSERT_EQ(0, fec_open(&handle, avb_image.path, O_RDWR, FEC_FS_EXT4, 2));
+ std::unique_ptr<fec_handle> guard(handle);
+
+ ASSERT_EQ(1024 * 1024, handle->data_size); // filesystem size
+ fec_ecc_metadata ecc_metadata{};
+ ASSERT_EQ(0, fec_ecc_get_metadata(handle, &ecc_metadata));
+ ASSERT_TRUE(ecc_metadata.valid);
+}
+
+TEST_F(FecUnitTest, AvbImage_FecRead) {
+ TemporaryFile avb_image;
+ ASSERT_TRUE(
+ android::base::WriteFully(avb_image.fd, image_.data(), image_.size()));
+ AddAvbHashtreeFooter(avb_image.path, "sha1");
+
+ uint64_t corrupt_offset = 4096 * 10;
+ ASSERT_EQ(corrupt_offset, lseek64(avb_image.fd, corrupt_offset, 0));
+ std::vector<uint8_t> corruption(50, 99);
+ ASSERT_TRUE(android::base::WriteFully(avb_image.fd, corruption.data(),
+ corruption.size()));
+
+ std::vector<uint8_t> read_data(1024, 0);
+ struct fec_handle *handle = nullptr;
+ ASSERT_EQ(0, fec_open(&handle, avb_image.path, O_RDWR, FEC_FS_EXT4, 2));
+ std::unique_ptr<fec_handle> guard(handle);
+
+ // Verify the hashtree has the expected content.
+ ASSERT_EQ(std::vector<uint8_t>(hashtree_content_.begin() + 4096,
+ hashtree_content_.end()),
+ handle->hashtree().hash_data);
+
+ // Verify the corruption gets corrected.
+ ASSERT_EQ(1024, fec_pread(handle, read_data.data(), 1024, corrupt_offset));
+ ASSERT_EQ(std::vector<uint8_t>(1024, 10), read_data);
+}
diff --git a/libfscrypt/fscrypt.cpp b/libfscrypt/fscrypt.cpp
index b8e6ddcf..622b4cdb 100644
--- a/libfscrypt/fscrypt.cpp
+++ b/libfscrypt/fscrypt.cpp
@@ -132,12 +132,6 @@ static bool fscrypt_is_encrypted(int fd) {
return ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY, &policy) == 0 || errno == EINVAL;
}
-bool operator!=(const EncryptionOptions& lhs, const EncryptionOptions& rhs) {
- return !((lhs.version == rhs.version) && (lhs.contents_mode == rhs.contents_mode) &&
- (lhs.filenames_mode == rhs.filenames_mode) && (lhs.flags == rhs.flags) &&
- (lhs.use_hw_wrapped_key == rhs.use_hw_wrapped_key));
-}
-
unsigned int GetFirstApiLevel() {
return android::base::GetUintProperty<unsigned int>("ro.product.first_api_level", 0);
}
diff --git a/libfscrypt/include/fscrypt/fscrypt.h b/libfscrypt/include/fscrypt/fscrypt.h
index c780c7ce..78b12560 100644
--- a/libfscrypt/include/fscrypt/fscrypt.h
+++ b/libfscrypt/include/fscrypt/fscrypt.h
@@ -61,6 +61,24 @@ bool ParseOptionsForApiLevel(unsigned int first_api_level, const std::string& op
bool EnsurePolicy(const EncryptionPolicy& policy, const std::string& directory);
+inline bool operator==(const EncryptionOptions& lhs, const EncryptionOptions& rhs) {
+ return (lhs.version == rhs.version) && (lhs.contents_mode == rhs.contents_mode) &&
+ (lhs.filenames_mode == rhs.filenames_mode) && (lhs.flags == rhs.flags) &&
+ (lhs.use_hw_wrapped_key == rhs.use_hw_wrapped_key);
+}
+
+inline bool operator!=(const EncryptionOptions& lhs, const EncryptionOptions& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const EncryptionPolicy& lhs, const EncryptionPolicy& rhs) {
+ return lhs.key_raw_ref == rhs.key_raw_ref && lhs.options == rhs.options;
+}
+
+inline bool operator!=(const EncryptionPolicy& lhs, const EncryptionPolicy& rhs) {
+ return !(lhs == rhs);
+}
+
} // namespace fscrypt
} // namespace android
diff --git a/libfscrypt/tests/fscrypt_test.cpp b/libfscrypt/tests/fscrypt_test.cpp
index 7149e7ca..457ac684 100644
--- a/libfscrypt/tests/fscrypt_test.cpp
+++ b/libfscrypt/tests/fscrypt_test.cpp
@@ -165,3 +165,27 @@ TEST(fscrypt, ParseOptions) {
EXPECT_FALSE(ParseOptionsForApiLevel(30, "aes-256-xts:aes-256-cts:blah", &dummy_options));
EXPECT_FALSE(ParseOptionsForApiLevel(30, "aes-256-xts:aes-256-cts:vblah", &dummy_options));
}
+
+TEST(fscrypt, ComparePolicies) {
+#define TEST_INEQUALITY(foo, field, value) { \
+ auto bar = foo; \
+ bar.field = value; \
+ EXPECT_NE(foo, bar); \
+}
+ EncryptionPolicy foo;
+ foo.key_raw_ref = "foo";
+ EncryptionOptions foo_options;
+ foo_options.version = 1;
+ foo_options.contents_mode = 1;
+ foo_options.filenames_mode = 1;
+ foo_options.flags = 1;
+ foo_options.use_hw_wrapped_key = true;
+ foo.options = foo_options;
+ EXPECT_EQ(foo, foo);
+ TEST_INEQUALITY(foo, key_raw_ref, "bar");
+ TEST_INEQUALITY(foo, options.version, 2);
+ TEST_INEQUALITY(foo, options.contents_mode, -1);
+ TEST_INEQUALITY(foo, options.filenames_mode, 3);
+ TEST_INEQUALITY(foo, options.flags, 0);
+ TEST_INEQUALITY(foo, options.use_hw_wrapped_key, false);
+}
diff --git a/libperfmgr/.clang-format b/libperfmgr/.clang-format
deleted file mode 100644
index 181b743d..00000000
--- a/libperfmgr/.clang-format
+++ /dev/null
@@ -1,11 +0,0 @@
-BasedOnStyle: Google
-AccessModifierOffset: -2
-AllowShortFunctionsOnASingleLine: Inline
-ColumnLimit: 80
-CommentPragmas: NOLINT:.*
-DerivePointerAlignment: false
-IndentWidth: 4
-PointerAlignment: Left
-TabWidth: 4
-UseTab: Never
-PenaltyExcessCharacter: 32
diff --git a/libperfmgr/Android.bp b/libperfmgr/Android.bp
deleted file mode 100644
index 168fa2c4..00000000
--- a/libperfmgr/Android.bp
+++ /dev/null
@@ -1,77 +0,0 @@
-//
-// Copyright (C) 2017 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-
-cc_defaults {
- name: "libperfmgr_defaults",
- local_include_dirs: ["include"],
- shared_libs: [
- "libbase",
- "libutils",
- ],
- static_libs: [
- "libjsoncpp",
- ],
- cflags: [
- "-Wall",
- "-Werror",
- ],
- tidy: true,
- tidy_checks: [
- "android-*",
- "cert-*",
- "clang-analyzer-security*",
- ],
- tidy_flags: [
- "-warnings-as-errors=android-*,clang-analyzer-security*,cert-*"
- ],
-}
-
-cc_library {
- name: "libperfmgr",
- vendor_available: true,
- defaults: ["libperfmgr_defaults"],
- export_include_dirs: ["include"],
- srcs: [
- "RequestGroup.cc",
- "Node.cc",
- "FileNode.cc",
- "PropertyNode.cc",
- "NodeLooperThread.cc",
- "HintManager.cc",
- ]
-}
-
-cc_test {
- name: "libperfmgr_test",
- defaults: ["libperfmgr_defaults"],
- static_libs: ["libperfmgr"],
- srcs: [
- "tests/RequestGroupTest.cc",
- "tests/FileNodeTest.cc",
- "tests/PropertyNodeTest.cc",
- "tests/NodeLooperThreadTest.cc",
- "tests/HintManagerTest.cc",
- ]
-}
-
-cc_binary {
- name: "perfmgr_config_verifier",
- defaults: ["libperfmgr_defaults"],
- static_libs: ["libperfmgr"],
- srcs: [
- "tools/ConfigVerifier.cc",
- ]
-}
diff --git a/libperfmgr/CPPLINT.cfg b/libperfmgr/CPPLINT.cfg
deleted file mode 100644
index 25e55c66..00000000
--- a/libperfmgr/CPPLINT.cfg
+++ /dev/null
@@ -1,2 +0,0 @@
-set noparent
-filter=-build/c++11,-build/include_order,-build/namespaces,-build/header_guard,-whitespace/indent,-whitespace/line_length
diff --git a/libperfmgr/FileNode.cc b/libperfmgr/FileNode.cc
deleted file mode 100644
index bc2b8a67..00000000
--- a/libperfmgr/FileNode.cc
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specic language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "libperfmgr"
-
-#include <android-base/chrono_utils.h>
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-
-#include "perfmgr/FileNode.h"
-
-namespace android {
-namespace perfmgr {
-
-FileNode::FileNode(std::string name, std::string node_path,
- std::vector<RequestGroup> req_sorted,
- std::size_t default_val_index, bool reset_on_init,
- bool hold_fd)
- : Node(std::move(name), std::move(node_path), std::move(req_sorted),
- default_val_index, reset_on_init),
- hold_fd_(hold_fd) {
- if (reset_on_init) {
- Update(false);
- }
-}
-
-std::chrono::milliseconds FileNode::Update(bool log_error) {
- std::size_t value_index = default_val_index_;
- std::chrono::milliseconds expire_time = std::chrono::milliseconds::max();
-
- // Find the highest outstanding request's expire time
- for (std::size_t i = 0; i < req_sorted_.size(); i++) {
- if (req_sorted_[i].GetExpireTime(&expire_time)) {
- value_index = i;
- break;
- }
- }
-
- // Update node only if request index changes
- if (value_index != current_val_index_) {
- const std::string& req_value =
- req_sorted_[value_index].GetRequestValue();
-
- android::base::Timer t;
- fd_.reset(TEMP_FAILURE_RETRY(
- open(node_path_.c_str(), O_WRONLY | O_CLOEXEC | O_TRUNC)));
-
- if (fd_ == -1 || !android::base::WriteStringToFd(req_value, fd_)) {
- if (log_error) {
- LOG(WARNING) << "Failed to write to node: " << node_path_
- << " with value: " << req_value << ", fd: " << fd_;
- }
- // Retry in 500ms or sooner
- expire_time = std::min(expire_time, std::chrono::milliseconds(500));
- } else {
- // For regular file system, we need fsync
- fsync(fd_);
- // Some dev node requires file to remain open during the entire hint
- // duration e.g. /dev/cpu_dma_latency, so fd_ is intentionally kept
- // open during any requested value other than default one. If
- // request a default value, node will write the value and then
- // release the fd.
- if ((!hold_fd_) || value_index == default_val_index_) {
- fd_.reset();
- }
- auto duration = t.duration();
- if (duration > 50ms) {
- LOG(WARNING) << "Slow writing to file: '" << node_path_
- << "' with value: '" << req_value
- << "' took: " << duration.count() << " ms";
- }
- // Update current index only when succeed
- current_val_index_ = value_index;
- }
- }
- return expire_time;
-}
-
-bool FileNode::GetHoldFd() const {
- return hold_fd_;
-}
-
-void FileNode::DumpToFd(int fd) const {
- std::string node_value;
- if (!android::base::ReadFileToString(node_path_, &node_value)) {
- LOG(ERROR) << "Failed to read node path: " << node_path_;
- }
- node_value = android::base::Trim(node_value);
- std::string buf(android::base::StringPrintf(
- "%s\t%s\t%zu\t%s\n", name_.c_str(), node_path_.c_str(),
- current_val_index_, node_value.c_str()));
- if (!android::base::WriteStringToFd(buf, fd)) {
- LOG(ERROR) << "Failed to dump fd: " << fd;
- }
-}
-
-} // namespace perfmgr
-} // namespace android
diff --git a/libperfmgr/HintManager.cc b/libperfmgr/HintManager.cc
deleted file mode 100644
index 4bb26b2b..00000000
--- a/libperfmgr/HintManager.cc
+++ /dev/null
@@ -1,379 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specic language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "libperfmgr"
-
-#include <algorithm>
-#include <set>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-
-#include <json/reader.h>
-#include <json/value.h>
-
-#include "perfmgr/FileNode.h"
-#include "perfmgr/HintManager.h"
-#include "perfmgr/PropertyNode.h"
-
-namespace android {
-namespace perfmgr {
-
-bool HintManager::ValidateHint(const std::string& hint_type) const {
- if (nm_.get() == nullptr) {
- LOG(ERROR) << "NodeLooperThread not present";
- return false;
- }
- if (actions_.find(hint_type) == actions_.end()) {
- LOG(ERROR) << "PowerHint type not present in actions: " << hint_type;
- return false;
- }
- return true;
-}
-
-bool HintManager::DoHint(const std::string& hint_type) {
- LOG(VERBOSE) << "Do Powerhint: " << hint_type;
- return ValidateHint(hint_type)
- ? nm_->Request(actions_.at(hint_type), hint_type)
- : false;
-}
-
-bool HintManager::DoHint(const std::string& hint_type,
- std::chrono::milliseconds timeout_ms_override) {
- LOG(VERBOSE) << "Do Powerhint: " << hint_type << " for "
- << timeout_ms_override.count() << "ms";
- if (!ValidateHint(hint_type)) {
- return false;
- }
- std::vector<NodeAction> actions_override = actions_.at(hint_type);
- for (auto& action : actions_override) {
- action.timeout_ms = timeout_ms_override;
- }
- return nm_->Request(actions_override, hint_type);
-}
-
-bool HintManager::EndHint(const std::string& hint_type) {
- LOG(VERBOSE) << "End Powerhint: " << hint_type;
- return ValidateHint(hint_type)
- ? nm_->Cancel(actions_.at(hint_type), hint_type)
- : false;
-}
-
-bool HintManager::IsRunning() const {
- return (nm_.get() == nullptr) ? false : nm_->isRunning();
-}
-
-std::vector<std::string> HintManager::GetHints() const {
- std::vector<std::string> hints;
- for (auto const& action : actions_) {
- hints.push_back(action.first);
- }
- return hints;
-}
-
-void HintManager::DumpToFd(int fd) {
- std::string header(
- "========== Begin perfmgr nodes ==========\n"
- "Node Name\t"
- "Node Path\t"
- "Current Index\t"
- "Current Value\n");
- if (!android::base::WriteStringToFd(header, fd)) {
- LOG(ERROR) << "Failed to dump fd: " << fd;
- }
- nm_->DumpToFd(fd);
- std::string footer("========== End perfmgr nodes ==========\n");
- if (!android::base::WriteStringToFd(footer, fd)) {
- LOG(ERROR) << "Failed to dump fd: " << fd;
- }
- fsync(fd);
-}
-
-std::unique_ptr<HintManager> HintManager::GetFromJSON(
- const std::string& config_path) {
- std::string json_doc;
-
- if (!android::base::ReadFileToString(config_path, &json_doc)) {
- LOG(ERROR) << "Failed to read JSON config from " << config_path;
- return nullptr;
- }
-
- std::vector<std::unique_ptr<Node>> nodes = ParseNodes(json_doc);
- if (nodes.empty()) {
- LOG(ERROR) << "Failed to parse Nodes section from " << config_path;
- return nullptr;
- }
- std::map<std::string, std::vector<NodeAction>> actions =
- HintManager::ParseActions(json_doc, nodes);
-
- if (actions.empty()) {
- LOG(ERROR) << "Failed to parse Actions section from " << config_path;
- return nullptr;
- }
-
- sp<NodeLooperThread> nm = new NodeLooperThread(std::move(nodes));
- std::unique_ptr<HintManager> hm =
- std::make_unique<HintManager>(std::move(nm), actions);
-
- LOG(INFO) << "Initialized HintManager from JSON config: " << config_path;
- return hm;
-}
-
-std::vector<std::unique_ptr<Node>> HintManager::ParseNodes(
- const std::string& json_doc) {
- // function starts
- std::vector<std::unique_ptr<Node>> nodes_parsed;
- std::set<std::string> nodes_name_parsed;
- std::set<std::string> nodes_path_parsed;
- Json::Value root;
- Json::Reader reader;
-
- if (!reader.parse(json_doc, root)) {
- LOG(ERROR) << "Failed to parse JSON config";
- return nodes_parsed;
- }
-
- Json::Value nodes = root["Nodes"];
- for (Json::Value::ArrayIndex i = 0; i < nodes.size(); ++i) {
- std::string name = nodes[i]["Name"].asString();
- LOG(VERBOSE) << "Node[" << i << "]'s Name: " << name;
- if (name.empty()) {
- LOG(ERROR) << "Failed to read "
- << "Node[" << i << "]'s Name";
- nodes_parsed.clear();
- return nodes_parsed;
- }
-
- auto result = nodes_name_parsed.insert(name);
- if (!result.second) {
- LOG(ERROR) << "Duplicate Node[" << i << "]'s Name";
- nodes_parsed.clear();
- return nodes_parsed;
- }
-
- std::string path = nodes[i]["Path"].asString();
- LOG(VERBOSE) << "Node[" << i << "]'s Path: " << path;
- if (path.empty()) {
- LOG(ERROR) << "Failed to read "
- << "Node[" << i << "]'s Path";
- nodes_parsed.clear();
- return nodes_parsed;
- }
-
- result = nodes_path_parsed.insert(path);
- if (!result.second) {
- LOG(ERROR) << "Duplicate Node[" << i << "]'s Path";
- nodes_parsed.clear();
- return nodes_parsed;
- }
-
- bool is_file = true;
- std::string node_type = nodes[i]["Type"].asString();
- LOG(VERBOSE) << "Node[" << i << "]'s Type: " << node_type;
- if (node_type.empty()) {
- LOG(ERROR) << "Failed to read "
- << "Node[" << i << "]'s Type, set to 'File' as default";
- } else if (node_type == "File") {
- is_file = true;
- } else if (node_type == "Property") {
- is_file = false;
- } else {
- LOG(ERROR) << "Invalid Node[" << i
- << "]'s Type: only File and Property supported.";
- nodes_parsed.clear();
- return nodes_parsed;
- }
-
- std::vector<RequestGroup> values_parsed;
- std::set<std::string> values_set_parsed;
- Json::Value values = nodes[i]["Values"];
- for (Json::Value::ArrayIndex j = 0; j < values.size(); ++j) {
- std::string value = values[j].asString();
- LOG(VERBOSE) << "Node[" << i << "]'s Value[" << j << "]: " << value;
- auto result = values_set_parsed.insert(value);
- if (!result.second) {
- LOG(ERROR) << "Duplicate value parsed in Node[" << i << "]'s Value[" << j
- << "]";
- nodes_parsed.clear();
- return nodes_parsed;
- }
- if (is_file && value.empty()) {
- LOG(ERROR) << "Failed to read Node[" << i << "]'s Value[" << j << "]";
- nodes_parsed.clear();
- return nodes_parsed;
- }
- values_parsed.emplace_back(value);
- }
- if (values_parsed.size() < 1) {
- LOG(ERROR) << "Failed to read Node[" << i << "]'s Values";
- nodes_parsed.clear();
- return nodes_parsed;
- }
-
- Json::UInt64 default_index = values_parsed.size() - 1;
- if (nodes[i]["DefaultIndex"].empty() ||
- !nodes[i]["DefaultIndex"].isUInt64()) {
- LOG(INFO) << "Failed to read Node[" << i
- << "]'s DefaultIndex, set to last index: "
- << default_index;
- } else {
- default_index = nodes[i]["DefaultIndex"].asUInt64();
- }
- if (default_index > values_parsed.size() - 1) {
- default_index = values_parsed.size() - 1;
- LOG(ERROR) << "Node[" << i
- << "]'s DefaultIndex out of bound, max value index: "
- << default_index;
- nodes_parsed.clear();
- return nodes_parsed;
- }
- LOG(VERBOSE) << "Node[" << i << "]'s DefaultIndex: " << default_index;
-
- bool reset = false;
- if (nodes[i]["ResetOnInit"].empty() ||
- !nodes[i]["ResetOnInit"].isBool()) {
- LOG(INFO) << "Failed to read Node[" << i
- << "]'s ResetOnInit, set to 'false'";
- } else {
- reset = nodes[i]["ResetOnInit"].asBool();
- }
- LOG(VERBOSE) << "Node[" << i << "]'s ResetOnInit: " << std::boolalpha
- << reset << std::noboolalpha;
-
- if (is_file) {
- bool hold_fd = false;
- if (nodes[i]["HoldFd"].empty() || !nodes[i]["HoldFd"].isBool()) {
- LOG(INFO) << "Failed to read Node[" << i
- << "]'s HoldFd, set to 'false'";
- } else {
- hold_fd = nodes[i]["HoldFd"].asBool();
- }
- LOG(VERBOSE) << "Node[" << i << "]'s HoldFd: " << std::boolalpha
- << hold_fd << std::noboolalpha;
-
- nodes_parsed.emplace_back(std::make_unique<FileNode>(
- name, path, values_parsed,
- static_cast<std::size_t>(default_index), reset, hold_fd));
- } else {
- nodes_parsed.emplace_back(std::make_unique<PropertyNode>(
- name, path, values_parsed,
- static_cast<std::size_t>(default_index), reset));
- }
- }
- LOG(INFO) << nodes_parsed.size() << " Nodes parsed successfully";
- return nodes_parsed;
-}
-
-std::map<std::string, std::vector<NodeAction>> HintManager::ParseActions(
- const std::string& json_doc,
- const std::vector<std::unique_ptr<Node>>& nodes) {
- // function starts
- std::map<std::string, std::vector<NodeAction>> actions_parsed;
- Json::Value root;
- Json::Reader reader;
-
- if (!reader.parse(json_doc, root)) {
- LOG(ERROR) << "Failed to parse JSON config";
- return actions_parsed;
- }
-
- Json::Value actions = root["Actions"];
- std::size_t total_parsed = 0;
-
- std::map<std::string, std::size_t> nodes_index;
- for (std::size_t i = 0; i < nodes.size(); ++i) {
- nodes_index[nodes[i]->GetName()] = i;
- }
-
- for (Json::Value::ArrayIndex i = 0; i < actions.size(); ++i) {
- const std::string& hint_type = actions[i]["PowerHint"].asString();
- LOG(VERBOSE) << "Action[" << i << "]'s PowerHint: " << hint_type;
- if (hint_type.empty()) {
- LOG(ERROR) << "Failed to read "
- << "Action[" << i << "]'s PowerHint";
- actions_parsed.clear();
- return actions_parsed;
- }
-
- std::string node_name = actions[i]["Node"].asString();
- LOG(VERBOSE) << "Action[" << i << "]'s Node: " << node_name;
- std::size_t node_index;
-
- if (nodes_index.find(node_name) == nodes_index.end()) {
- LOG(ERROR) << "Failed to find "
- << "Action[" << i
- << "]'s Node from Nodes section: [" << node_name << "]";
- actions_parsed.clear();
- return actions_parsed;
- }
- node_index = nodes_index[node_name];
-
- std::string value_name = actions[i]["Value"].asString();
- LOG(VERBOSE) << "Action[" << i << "]'s Value: " << value_name;
- std::size_t value_index = 0;
-
- if (!nodes[node_index]->GetValueIndex(value_name, &value_index)) {
- LOG(ERROR) << "Failed to read Action[" << i << "]'s Value";
- LOG(ERROR) << "Action[" << i << "]'s Value " << value_name
- << " is not defined in Node[" << node_name << "]";
- actions_parsed.clear();
- return actions_parsed;
- }
- LOG(VERBOSE) << "Action[" << i << "]'s ValueIndex: " << value_index;
-
- Json::UInt64 duration = 0;
- if (actions[i]["Duration"].empty() ||
- !actions[i]["Duration"].isUInt64()) {
- LOG(ERROR) << "Failed to read Action[" << i << "]'s Duration";
- actions_parsed.clear();
- return actions_parsed;
- } else {
- duration = actions[i]["Duration"].asUInt64();
- }
- LOG(VERBOSE) << "Action[" << i << "]'s Duration: " << duration;
-
- if (actions_parsed.find(hint_type) == actions_parsed.end()) {
- actions_parsed[hint_type] = std::vector<NodeAction>{
- {node_index, value_index, std::chrono::milliseconds(duration)}};
- } else {
- for (const auto& action : actions_parsed[hint_type]) {
- if (action.node_index == node_index) {
- LOG(ERROR)
- << "Action[" << i
- << "]'s NodeIndex is duplicated with another Action";
- actions_parsed.clear();
- return actions_parsed;
- }
- }
- actions_parsed[hint_type].emplace_back(
- node_index, value_index, std::chrono::milliseconds(duration));
- }
-
- ++total_parsed;
- }
-
- LOG(INFO) << total_parsed << " Actions parsed successfully";
-
- for (const auto& action : actions_parsed) {
- LOG(INFO) << "PowerHint " << action.first << " has "
- << action.second.size() << " actions parsed";
- }
-
- return actions_parsed;
-}
-
-} // namespace perfmgr
-} // namespace android
diff --git a/libperfmgr/Node.cc b/libperfmgr/Node.cc
deleted file mode 100644
index 8c13dae4..00000000
--- a/libperfmgr/Node.cc
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specic language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "libperfmgr"
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-
-#include "perfmgr/Node.h"
-
-namespace android {
-namespace perfmgr {
-
-Node::Node(std::string name, std::string node_path,
- std::vector<RequestGroup> req_sorted, std::size_t default_val_index,
- bool reset_on_init)
- : name_(std::move(name)),
- node_path_(std::move(node_path)),
- req_sorted_(std::move(req_sorted)),
- default_val_index_(default_val_index),
- reset_on_init_(reset_on_init),
- // Assigning an invalid value so the next Update() will update the
- // Node's value to default
- current_val_index_(reset_on_init ? req_sorted_.size()
- : default_val_index) {}
-
-bool Node::AddRequest(std::size_t value_index, const std::string& hint_type,
- ReqTime end_time) {
- if (value_index >= req_sorted_.size()) {
- LOG(ERROR) << "Value index out of bound: " << value_index
- << " ,size: " << req_sorted_.size();
- return false;
- }
- // Add/Update request to the new end_time for the specific hint_type
- req_sorted_[value_index].AddRequest(hint_type, end_time);
- return true;
-}
-
-bool Node::RemoveRequest(const std::string& hint_type) {
- bool ret = false;
- // Remove all requests for the specific hint_type
- for (auto& value : req_sorted_) {
- ret = value.RemoveRequest(hint_type) || ret;
- }
- return ret;
-}
-
-const std::string& Node::GetName() const {
- return name_;
-}
-
-const std::string& Node::GetPath() const {
- return node_path_;
-}
-
-bool Node::GetValueIndex(const std::string& value, std::size_t* index) const {
- bool found = false;
- for (std::size_t i = 0; i < req_sorted_.size(); i++) {
- if (req_sorted_[i].GetRequestValue() == value) {
- *index = i;
- found = true;
- break;
- }
- }
- return found;
-}
-
-std::size_t Node::GetDefaultIndex() const {
- return default_val_index_;
-}
-
-bool Node::GetResetOnInit() const {
- return reset_on_init_;
-}
-
-std::vector<std::string> Node::GetValues() const {
- std::vector<std::string> values;
- for (const auto& value : req_sorted_) {
- values.emplace_back(value.GetRequestValue());
- }
- return values;
-}
-
-} // namespace perfmgr
-} // namespace android
diff --git a/libperfmgr/NodeLooperThread.cc b/libperfmgr/NodeLooperThread.cc
deleted file mode 100644
index eb5c9b43..00000000
--- a/libperfmgr/NodeLooperThread.cc
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specic language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "libperfmgr"
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-
-#include "perfmgr/NodeLooperThread.h"
-
-namespace android {
-namespace perfmgr {
-
-bool NodeLooperThread::Request(const std::vector<NodeAction>& actions,
- const std::string& hint_type) {
- if (::android::Thread::exitPending()) {
- LOG(WARNING) << "NodeLooperThread is exiting";
- return false;
- }
- if (!::android::Thread::isRunning()) {
- LOG(FATAL) << "NodeLooperThread stopped, abort...";
- }
-
- bool ret = true;
- ::android::AutoMutex _l(lock_);
- for (const auto& a : actions) {
- if (a.node_index >= nodes_.size()) {
- LOG(ERROR) << "Node index out of bound: " << a.node_index
- << " ,size: " << nodes_.size();
- ret = false;
- } else {
- // End time set to steady time point max
- ReqTime end_time = ReqTime::max();
- // Timeout is non-zero
- if (a.timeout_ms != std::chrono::milliseconds::zero()) {
- auto now = std::chrono::steady_clock::now();
- // Overflow protection in case timeout_ms is too big to overflow
- // time point which is unsigned integer
- if (std::chrono::duration_cast<std::chrono::milliseconds>(
- ReqTime::max() - now) > a.timeout_ms) {
- end_time = now + a.timeout_ms;
- }
- }
- ret = nodes_[a.node_index]->AddRequest(a.value_index, hint_type,
- end_time) &&
- ret;
- }
- }
- wake_cond_.signal();
- return ret;
-}
-
-bool NodeLooperThread::Cancel(const std::vector<NodeAction>& actions,
- const std::string& hint_type) {
- if (::android::Thread::exitPending()) {
- LOG(WARNING) << "NodeLooperThread is exiting";
- return false;
- }
- if (!::android::Thread::isRunning()) {
- LOG(FATAL) << "NodeLooperThread stopped, abort...";
- }
-
- bool ret = true;
- ::android::AutoMutex _l(lock_);
- for (const auto& a : actions) {
- if (a.node_index >= nodes_.size()) {
- LOG(ERROR) << "Node index out of bound: " << a.node_index
- << " ,size: " << nodes_.size();
- ret = false;
- } else {
- nodes_[a.node_index]->RemoveRequest(hint_type);
- }
- }
- wake_cond_.signal();
- return ret;
-}
-
-void NodeLooperThread::DumpToFd(int fd) {
- ::android::AutoMutex _l(lock_);
- for (auto& n : nodes_) {
- n->DumpToFd(fd);
- }
-}
-
-bool NodeLooperThread::threadLoop() {
- ::android::AutoMutex _l(lock_);
- std::chrono::milliseconds timeout_ms = kMaxUpdatePeriod;
-
- // Update 2 passes: some node may have dependency in other node
- // e.g. update cpufreq min to VAL while cpufreq max still set to
- // a value lower than VAL, is expected to fail in first pass
- for (auto& n : nodes_) {
- n->Update(false);
- }
- for (auto& n : nodes_) {
- timeout_ms = std::min(n->Update(true), timeout_ms);
- }
-
- nsecs_t sleep_timeout_ns = std::numeric_limits<nsecs_t>::max();
- if (timeout_ms.count() < sleep_timeout_ns / 1000 / 1000) {
- sleep_timeout_ns = timeout_ms.count() * 1000 * 1000;
- }
- // VERBOSE level won't print by default in user/userdebug build
- LOG(VERBOSE) << "NodeLooperThread will wait for " << sleep_timeout_ns
- << "ns";
- wake_cond_.waitRelative(lock_, sleep_timeout_ns);
- return true;
-}
-
-void NodeLooperThread::onFirstRef() {
- auto ret = this->run("NodeLooperThread", PRIORITY_HIGHEST);
- if (ret != NO_ERROR) {
- LOG(ERROR) << "NodeLooperThread start fail";
- } else {
- LOG(INFO) << "NodeLooperThread started";
- }
-}
-
-void NodeLooperThread::Stop() {
- if (::android::Thread::isRunning()) {
- LOG(INFO) << "NodeLooperThread stopping";
- {
- ::android::AutoMutex _l(lock_);
- wake_cond_.signal();
- ::android::Thread::requestExit();
- }
- ::android::Thread::join();
- LOG(INFO) << "NodeLooperThread stopped";
- }
-}
-
-} // namespace perfmgr
-} // namespace android
diff --git a/libperfmgr/OWNERS b/libperfmgr/OWNERS
deleted file mode 100644
index 71f03104..00000000
--- a/libperfmgr/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-wvw@google.com
-joaodias@google.com
-migueldedios@google.com
diff --git a/libperfmgr/PropertyNode.cc b/libperfmgr/PropertyNode.cc
deleted file mode 100644
index 029849d6..00000000
--- a/libperfmgr/PropertyNode.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specic language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "libperfmgr"
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
-
-#include "perfmgr/PropertyNode.h"
-
-namespace android {
-namespace perfmgr {
-
-PropertyNode::PropertyNode(std::string name, std::string node_path,
- std::vector<RequestGroup> req_sorted,
- std::size_t default_val_index, bool reset_on_init)
- : Node(std::move(name), std::move(node_path), std::move(req_sorted),
- default_val_index, reset_on_init) {
- if (reset_on_init) {
- Update(false);
- }
-}
-
-std::chrono::milliseconds PropertyNode::Update(bool) {
- std::size_t value_index = default_val_index_;
- std::chrono::milliseconds expire_time = std::chrono::milliseconds::max();
-
- // Find the highest outstanding request's expire time
- for (std::size_t i = 0; i < req_sorted_.size(); i++) {
- if (req_sorted_[i].GetExpireTime(&expire_time)) {
- value_index = i;
- break;
- }
- }
-
- // Update node only if request index changes
- if (value_index != current_val_index_) {
- const std::string& req_value =
- req_sorted_[value_index].GetRequestValue();
-
- if (!android::base::SetProperty(node_path_, req_value)) {
- LOG(WARNING) << "Failed to set property to : " << node_path_
- << " with value: " << req_value;
- } else {
- // Update current index only when succeed
- current_val_index_ = value_index;
- }
- }
- return expire_time;
-}
-
-void PropertyNode::DumpToFd(int fd) const {
- std::string node_value = android::base::GetProperty(node_path_, "");
- std::string buf(android::base::StringPrintf(
- "%s\t%s\t%zu\t%s\n", name_.c_str(), node_path_.c_str(),
- current_val_index_, node_value.c_str()));
- if (!android::base::WriteStringToFd(buf, fd)) {
- LOG(ERROR) << "Failed to dump fd: " << fd;
- }
-}
-
-} // namespace perfmgr
-} // namespace android
diff --git a/libperfmgr/RequestGroup.cc b/libperfmgr/RequestGroup.cc
deleted file mode 100644
index 857d17a4..00000000
--- a/libperfmgr/RequestGroup.cc
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specic language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "libperfmgr"
-
-#include "perfmgr/RequestGroup.h"
-
-namespace android {
-namespace perfmgr {
-
-bool RequestGroup::AddRequest(const std::string& hint_type, ReqTime end_time) {
- if (request_map_.find(hint_type) == request_map_.end()) {
- request_map_.emplace(hint_type, end_time);
- return true;
- } else {
- if (request_map_[hint_type] < end_time) {
- request_map_[hint_type] = end_time;
- }
- return false;
- }
-}
-
-bool RequestGroup::RemoveRequest(const std::string& hint_type) {
- return request_map_.erase(hint_type);
-}
-
-const std::string& RequestGroup::GetRequestValue() const {
- return request_value_;
-}
-
-bool RequestGroup::GetExpireTime(std::chrono::milliseconds* expire_time) {
- ReqTime now = std::chrono::steady_clock::now();
- *expire_time = std::chrono::milliseconds::max();
-
- bool active = false;
- for (auto it = request_map_.begin(); it != request_map_.end();) {
- auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
- it->second - now);
- if (duration <= std::chrono::milliseconds::zero()) {
- it = request_map_.erase(it);
- } else {
- *expire_time = std::min(duration, *expire_time);
- active = true;
- ++it;
- }
- }
- return active;
-}
-
-} // namespace perfmgr
-} // namespace android
diff --git a/libperfmgr/config_schema.json b/libperfmgr/config_schema.json
deleted file mode 100644
index 712af500..00000000
--- a/libperfmgr/config_schema.json
+++ /dev/null
@@ -1,126 +0,0 @@
-{
- "definitions": {},
- "$schema": "http://json-schema.org/draft-06/schema#",
- "type": "object",
- "id": "config_schema.json",
- "required": [
- "Nodes",
- "Actions"
- ],
- "properties": {
- "Nodes": {
- "type": "array",
- "id": "/properties/Nodes",
- "minItems": 1,
- "uniqueItems": true,
- "items": {
- "type": "object",
- "id": "/properties/Nodes/items",
- "required": [
- "Name",
- "Path",
- "Values"
- ],
- "properties": {
- "Name": {
- "type": "string",
- "id": "/properties/Nodes/items/properties/Name",
- "title": "The Name Schema.",
- "description": "The name of the node.",
- "minLength": 1
- },
- "Path": {
- "type": "string",
- "id": "/properties/Nodes/items/properties/Path",
- "title": "The Path Schema.",
- "description": "For File type node, it is filesystem path of the file; for Property type node, it is the key of the property.",
- "minLength": 1
- },
- "Values": {
- "type": "array",
- "id": "/properties/Nodes/items/properties/Values",
- "minItems": 1,
- "uniqueItems": true,
- "items": {
- "type": "string",
- "id": "/properties/Nodes/items/properties/Values/items",
- "title": "The Values Schema.",
- "description": "The Values array lists all possible values that can be set in the Actions section, and the list of values is sorted based on their priority, with the highest priority first."
- }
- },
- "DefaultIndex": {
- "type": "integer",
- "id": "/properties/Nodes/items/properties/DefaultIndex",
- "title": "The Default Index Schema.",
- "description": "The default index of the node, if not present, it will be set to max index of Values.",
- "minimum": 0
- },
- "ResetOnInit": {
- "type": "boolean",
- "id": "/properties/Nodes/items/properties/ResetOnInit",
- "title": "The Reset On Init Schema.",
- "description": "Flag if node will be set to default value on initialization; if not present, it will be set to false."
- },
- "Type": {
- "type": "string",
- "id": "/properties/Nodes/items/properties/Type",
- "title": "The type Schema.",
- "description": "Type of Node (File or Property), if not present, it will be set to File."
- },
- "HoldFd": {
- "type": "boolean",
- "id": "/properties/Nodes/items/properties/HoldFd",
- "title": "The Hold Fd Schema.",
- "description": "Flag if node will hold the file descriptor on non-default values; if not present, it will be set to false. This is only honoured for File type node."
- }
- }
- }
- },
- "Actions": {
- "type": "array",
- "id": "/properties/Actions",
- "minItems": 1,
- "uniqueItems": true,
- "items": {
- "type": "object",
- "id": "/properties/Actions/items",
- "required": [
- "PowerHint",
- "Node",
- "ValueIndex",
- "Duration"
- ],
- "properties": {
- "PowerHint": {
- "type": "string",
- "id": "/properties/Actions/items/properties/PowerHint",
- "title": "The PowerHint Schema.",
- "description": "The PowerHint name of the action.",
- "minLength": 1
- },
- "Node": {
- "type": "string",
- "id": "/properties/Actions/items/properties/Node",
- "title": "The Node Schema.",
- "description": "The Node name of the action, which is defined in Nodes.",
- "minLength": 1
- },
- "Value": {
- "type": "string",
- "id": "/properties/Actions/items/properties/Value",
- "title": "The Value Index Schema.",
- "description": "The value of action, which is defined in Nodes.",
- "minLength": 1
- },
- "Duration": {
- "type": "integer",
- "id": "/properties/Actions/items/properties/Duration",
- "title": "The Duration Schema.",
- "description": "The number of milliseconds that this action will be active (zero means forever).",
- "minimum": 0
- }
- }
- }
- }
- }
-}
diff --git a/libperfmgr/include/perfmgr/FileNode.h b/libperfmgr/include/perfmgr/FileNode.h
deleted file mode 100644
index 1365df17..00000000
--- a/libperfmgr/include/perfmgr/FileNode.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specic language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_LIBPERFMGR_FILENODE_H_
-#define ANDROID_LIBPERFMGR_FILENODE_H_
-
-#include <cstddef>
-#include <string>
-#include <vector>
-
-#include <android-base/unique_fd.h>
-
-#include "perfmgr/Node.h"
-
-namespace android {
-namespace perfmgr {
-
-// FileNode represents file
-class FileNode : public Node {
- public:
- FileNode(std::string name, std::string node_path,
- std::vector<RequestGroup> req_sorted, std::size_t default_val_index,
- bool reset_on_init, bool hold_fd = false);
-
- std::chrono::milliseconds Update(bool log_error) override;
-
- bool GetHoldFd() const;
-
- void DumpToFd(int fd) const override;
-
- private:
- FileNode(const Node& other) = delete;
- FileNode& operator=(Node const&) = delete;
-
- const bool hold_fd_;
- android::base::unique_fd fd_;
-};
-
-} // namespace perfmgr
-} // namespace android
-
-#endif // ANDROID_LIBPERFMGR_FILENODE_H_
diff --git a/libperfmgr/include/perfmgr/HintManager.h b/libperfmgr/include/perfmgr/HintManager.h
deleted file mode 100644
index 7932ec21..00000000
--- a/libperfmgr/include/perfmgr/HintManager.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specic language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_LIBPERFMGR_HINTMANAGER_H_
-#define ANDROID_LIBPERFMGR_HINTMANAGER_H_
-
-#include <cstddef>
-#include <map>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "perfmgr/NodeLooperThread.h"
-
-namespace android {
-namespace perfmgr {
-
-// HintManager is the external interface of the library to be used by PowerHAL
-// to do power hints with sysfs nodes. HintManager maintains a representation of
-// the actions that are parsed from the configuration file as a mapping from a
-// PowerHint to the set of actions that are performed for that PowerHint.
-class HintManager {
- public:
- HintManager(sp<NodeLooperThread> nm,
- const std::map<std::string, std::vector<NodeAction>>& actions)
- : nm_(std::move(nm)), actions_(actions) {}
- ~HintManager() {
- if (nm_.get() != nullptr) nm_->Stop();
- }
-
- // Return true if the sysfs manager thread is running.
- bool IsRunning() const;
-
- // Do hint based on hint_type which defined as PowerHint in the actions
- // section of the JSON config. Return true with valid hint_type and also
- // NodeLooperThread::Request succeeds; otherwise return false.
- bool DoHint(const std::string& hint_type);
-
- // Do hint with the override time for all actions defined for the given
- // hint_type. Return true with valid hint_type and also
- // NodeLooperThread::Request succeeds; otherwise return false.
- bool DoHint(const std::string& hint_type,
- std::chrono::milliseconds timeout_ms_override);
-
- // End hint early. Return true with valid hint_type and also
- // NodeLooperThread::Cancel succeeds; otherwise return false.
- bool EndHint(const std::string& hint_type);
-
- // Static method to construct HintManager from the JSON config file.
- static std::unique_ptr<HintManager> GetFromJSON(
- const std::string& config_path);
-
- // Return available hints managed by HintManager
- std::vector<std::string> GetHints() const;
-
- // Dump internal status to fd
- void DumpToFd(int fd);
-
- protected:
- static std::vector<std::unique_ptr<Node>> ParseNodes(
- const std::string& json_doc);
- static std::map<std::string, std::vector<NodeAction>> ParseActions(
- const std::string& json_doc,
- const std::vector<std::unique_ptr<Node>>& nodes);
-
- private:
- HintManager(HintManager const&) = delete;
- void operator=(HintManager const&) = delete;
- bool ValidateHint(const std::string& hint_type) const;
-
- sp<NodeLooperThread> nm_;
- const std::map<std::string, std::vector<NodeAction>> actions_;
-};
-
-} // namespace perfmgr
-} // namespace android
-
-#endif // ANDROID_LIBPERFMGR_HINTMANAGER_H_
diff --git a/libperfmgr/include/perfmgr/Node.h b/libperfmgr/include/perfmgr/Node.h
deleted file mode 100644
index cd0fba82..00000000
--- a/libperfmgr/include/perfmgr/Node.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specic language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_LIBPERFMGR_NODE_H_
-#define ANDROID_LIBPERFMGR_NODE_H_
-
-#include <cstddef>
-#include <string>
-#include <vector>
-
-#include <android-base/unique_fd.h>
-
-#include "perfmgr/RequestGroup.h"
-
-namespace android {
-namespace perfmgr {
-
-// The Node class provides an interface for adding and cancelling powerhint
-// requests, as well as checking the next time that an in-progress powerhint
-// request will expire. There are additional methods for getting the Node’s name
-// and the index of a value, which may be used for initialization, debugging,
-// and request management. The core of the Node class is a vector of
-// RequestGroups named req_sorted_, which is used to track the in-progress
-// requests on the node. Each entry in the vector corresponds to a possible
-// value for the node, in priority order. For example, the first entry in the
-// vector for the cpu0 cluster represents the in-progress requests to boost the
-// cluster’s frequency to the highest available value. The next entry represents
-// the in-progress requests to boost the cluster’s frequency to the next highest
-// value. For each value, there may be multiple requests because different
-// powerhints may request the same value, and the requests may have different
-// expiration times. All of the in-progress powerhints for a given value are
-// collected in a RequestGroup. Node class is not thread safe so it needs
-// protection from caller e.g. NodeLooperThread.
-class Node {
- public:
- virtual ~Node() {}
-
- // Return true if successfully add a request
- bool AddRequest(std::size_t value_index, const std::string& hint_type,
- ReqTime end_time);
-
- // Return true if successfully remove a request
- bool RemoveRequest(const std::string& hint_type);
-
- // Return the nearest expire time of active requests; return
- // std::chrono::milliseconds::max() if no active request on Node; update
- // node's controlled file node value and the current value index based on
- // active request.
- virtual std::chrono::milliseconds Update(bool log_error) = 0;
-
- const std::string& GetName() const;
- const std::string& GetPath() const;
- std::vector<std::string> GetValues() const;
- std::size_t GetDefaultIndex() const;
- bool GetResetOnInit() const;
- bool GetValueIndex(const std::string& value, std::size_t* index) const;
- virtual void DumpToFd(int fd) const = 0;
-
- protected:
- Node(std::string name, std::string node_path,
- std::vector<RequestGroup> req_sorted, std::size_t default_val_index,
- bool reset_on_init);
- Node(const Node& other) = delete;
- Node& operator=(Node const&) = delete;
-
- const std::string name_;
- const std::string node_path_;
- // request vector, one entry per possible value, sorted by priority
- std::vector<RequestGroup> req_sorted_;
- const std::size_t default_val_index_;
- const bool reset_on_init_;
- std::size_t current_val_index_;
-};
-
-} // namespace perfmgr
-} // namespace android
-
-#endif // ANDROID_LIBPERFMGR_NODE_H_
diff --git a/libperfmgr/include/perfmgr/NodeLooperThread.h b/libperfmgr/include/perfmgr/NodeLooperThread.h
deleted file mode 100644
index b04d2a40..00000000
--- a/libperfmgr/include/perfmgr/NodeLooperThread.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specic language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_LIBPERFMGR_NODELOOPERTHREAD_H_
-#define ANDROID_LIBPERFMGR_NODELOOPERTHREAD_H_
-
-#include <cstddef>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include <utils/Thread.h>
-
-#include "perfmgr/Node.h"
-
-namespace android {
-namespace perfmgr {
-
-// The NodeAction specifies the sysfs node, the value to be assigned, and the
-// timeout for this action:
-struct NodeAction {
- NodeAction(std::size_t node_index, std::size_t value_index,
- std::chrono::milliseconds timeout_ms)
- : node_index(node_index),
- value_index(value_index),
- timeout_ms(timeout_ms) {}
- std::size_t node_index;
- std::size_t value_index;
- std::chrono::milliseconds timeout_ms; // 0ms for forever
-};
-
-// The NodeLooperThread is responsible for managing each of the sysfs nodes
-// specified in the configuration. At initialization, the NodeLooperThrea holds
-// a vector containing the nodes defined in the configuration. The NodeManager
-// gets powerhint requests and cancellations from the HintManager, maintains
-// state about the current set of powerhint requests on each sysfs node, and
-// decides how to apply the requests. The NodeLooperThread contains a ThreadLoop
-// to maintain the sysfs nodes, and that thread is woken up both to handle
-// powerhint requests and when the timeout expires for an in-progress powerhint.
-class NodeLooperThread : public ::android::Thread {
- public:
- explicit NodeLooperThread(std::vector<std::unique_ptr<Node>> nodes)
- : Thread(false), nodes_(std::move(nodes)) {}
- virtual ~NodeLooperThread() { Stop(); }
-
- // Need call Stop() as the threadloop will hold a strong pointer
- // itself and wait for Condition fired or timeout (60s) before
- // the out looper can call deconstructor to Stop() thread
- void Stop();
-
- // Return true when successfully adds request from actions for the hint_type
- // in each individual node. Return false if any of the actions has either
- // invalid node index or value index.
- bool Request(const std::vector<NodeAction>& actions,
- const std::string& hint_type);
- // Return when successfully cancels request from actions for the hint_type
- // in each individual node. Return false if any of the actions has invalid
- // node index.
- bool Cancel(const std::vector<NodeAction>& actions,
- const std::string& hint_type);
-
- // Dump all nodes to fd
- void DumpToFd(int fd);
-
- private:
- NodeLooperThread(NodeLooperThread const&) = delete;
- void operator=(NodeLooperThread const&) = delete;
- bool threadLoop() override;
- void onFirstRef() override;
-
- static constexpr auto kMaxUpdatePeriod = std::chrono::milliseconds::max();
-
- std::vector<std::unique_ptr<Node>> nodes_; // parsed from Config
-
- // conditional variable from C++ standard library can be affected by wall
- // time change as it is using CLOCK_REAL (b/35756266). The component should
- // not be impacted by wall time, thus need use Android specific Condition
- // class for waking up threadloop.
- ::android::Condition wake_cond_;
-
- // lock to protect nodes_
- ::android::Mutex lock_;
-};
-
-} // namespace perfmgr
-} // namespace android
-
-#endif // ANDROID_LIBPERFMGR_NODELOOPERTHREAD_H_
diff --git a/libperfmgr/include/perfmgr/PropertyNode.h b/libperfmgr/include/perfmgr/PropertyNode.h
deleted file mode 100644
index feaf85fa..00000000
--- a/libperfmgr/include/perfmgr/PropertyNode.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specic language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_LIBPERFMGR_PROPERTYNODE_H_
-#define ANDROID_LIBPERFMGR_PROPERTYNODE_H_
-
-#include <cstddef>
-#include <string>
-#include <vector>
-
-#include "perfmgr/Node.h"
-
-namespace android {
-namespace perfmgr {
-
-// PropertyNode represents managed system properties
-class PropertyNode : public Node {
- public:
- PropertyNode(std::string name, std::string node_path,
- std::vector<RequestGroup> req_sorted,
- std::size_t default_val_index, bool reset_on_init);
-
- std::chrono::milliseconds Update(bool log_error) override;
-
- void DumpToFd(int fd) const override;
-
- private:
- PropertyNode(const Node& other) = delete;
- PropertyNode& operator=(Node const&) = delete;
-};
-
-} // namespace perfmgr
-} // namespace android
-
-#endif // ANDROID_LIBPERFMGR_PROPERTYNODE_H_
diff --git a/libperfmgr/include/perfmgr/RequestGroup.h b/libperfmgr/include/perfmgr/RequestGroup.h
deleted file mode 100644
index 2d997b59..00000000
--- a/libperfmgr/include/perfmgr/RequestGroup.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specic language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_LIBPERFMGR_REQUESTGROUP_H_
-#define ANDROID_LIBPERFMGR_REQUESTGROUP_H_
-
-#include <chrono>
-#include <map>
-#include <string>
-#include <utility>
-
-namespace android {
-namespace perfmgr {
-
-using ReqTime = std::chrono::time_point<std::chrono::steady_clock>;
-
-// The RequestGroup type represents the set of requests for a given value on a
-// particular sysfs node, and the interface is simple: there is a function to
-// add requests, a function to remove requests, and a function to check for the
-// next expiration time if there is an outstanding request, and a function to
-// check the requested value. There may only be one request per PowerHint, so
-// the representation is simple: a map from PowerHint to the expiration time for
-// that hint.
-class RequestGroup {
- public:
- RequestGroup(std::string request_value) // NOLINT(runtime/explicit)
- : request_value_(std::move(request_value)) {}
-
- // Remove expired request in the map and return true when request_map_ is
- // not empty, false when request_map_ is empty; also update expire_time with
- // nearest timeout in request_map_ or std::chrono::milliseconds::max() when
- // request_map_ is empty.
- bool GetExpireTime(std::chrono::milliseconds* expire_time);
- // Return the request value.
- const std::string& GetRequestValue() const;
- // Return true for adding request, false for extending expire time of
- // existing active request on given hint_type.
- bool AddRequest(const std::string& hint_type, ReqTime end_time);
- // Return true for removing request, false if request is not active on given
- // hint_type. If request exits and the new end_time is less than the active
- // time, expire time will not be updated; also returns false.
- bool RemoveRequest(const std::string& hint_type);
-
- private:
- const std::string request_value_;
- std::map<std::string, ReqTime> request_map_;
-};
-
-} // namespace perfmgr
-} // namespace android
-
-#endif // ANDROID_LIBPERFMGR_REQUESTGROUP_H_
diff --git a/libperfmgr/tests/FileNodeTest.cc b/libperfmgr/tests/FileNodeTest.cc
deleted file mode 100644
index 04d7345b..00000000
--- a/libperfmgr/tests/FileNodeTest.cc
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specic language governing permissions and
- * limitations under the License.
- */
-
-#include <algorithm>
-#include <thread>
-
-#include <android-base/file.h>
-#include <android-base/stringprintf.h>
-
-#include <gtest/gtest.h>
-
-#include "perfmgr/FileNode.h"
-
-namespace android {
-namespace perfmgr {
-
-using namespace std::chrono_literals;
-
-constexpr double kTIMING_TOLERANCE_MS = std::chrono::milliseconds(25).count();
-constexpr auto kSLEEP_TOLERANCE_MS = 2ms;
-
-static inline void _VerifyPathValue(const std::string& path,
- const std::string& value) {
- std::string s;
- EXPECT_TRUE(android::base::ReadFileToString(path, &s)) << strerror(errno);
- EXPECT_EQ(value, s);
-}
-
-// Test init with no default value
-TEST(FileNodeTest, NoInitDefaultTest) {
- TemporaryFile tf;
- FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, false);
- _VerifyPathValue(tf.path, "");
-}
-
-// Test init with default value
-TEST(FileNodeTest, InitDefaultTest) {
- TemporaryFile tf;
- FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, true);
- _VerifyPathValue(tf.path, "value1");
- TemporaryFile tf2;
- FileNode t2("t2", tf2.path, {{"value0"}, {"value1"}, {"value2"}}, 0, true);
- _VerifyPathValue(tf2.path, "value0");
-}
-
-// Test DumpToFd
-TEST(FileNodeTest, DumpToFdTest) {
- TemporaryFile tf;
- FileNode t("test_dump", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1,
- true);
- TemporaryFile dumptf;
- t.DumpToFd(dumptf.fd);
- fsync(dumptf.fd);
- std::string buf(
- android::base::StringPrintf("test_dump\t%s\t1\tvalue1\n", tf.path));
- _VerifyPathValue(dumptf.path, buf);
-}
-
-// Test GetValueIndex
-TEST(FileNodeTest, GetValueIndexTest) {
- TemporaryFile tf;
- FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, false);
- std::size_t index = 0;
- EXPECT_TRUE(t.GetValueIndex("value2", &index));
- EXPECT_EQ(2u, index);
- index = 1234;
- EXPECT_FALSE(t.GetValueIndex("NON_EXIST", &index));
- EXPECT_EQ(1234u, index);
-}
-
-// Test GetValues
-TEST(FileNodeTest, GetValuesTest) {
- TemporaryFile tf;
- FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, false);
- std::vector values = t.GetValues();
- EXPECT_EQ(3u, values.size());
- EXPECT_EQ("value0", values[0]);
- EXPECT_EQ("value1", values[1]);
- EXPECT_EQ("value2", values[2]);
-}
-
-// Test get more properties
-TEST(FileNodeTest, GetPropertiesTest) {
- std::string test_name = "TESTREQ_1";
- std::string test_path = "TEST_PATH";
- FileNode t(test_name, test_path, {}, 0, false, true);
- EXPECT_EQ(test_name, t.GetName());
- EXPECT_EQ(test_path, t.GetPath());
- EXPECT_EQ(0u, t.GetValues().size());
- EXPECT_EQ(0u, t.GetDefaultIndex());
- EXPECT_FALSE(t.GetResetOnInit());
- EXPECT_TRUE(t.GetHoldFd());
-}
-
-// Test add request fail and retry
-TEST(FileNodeTest, AddRequestTestFail) {
- FileNode t("t", "/sys/android/nonexist_node_test",
- {{"value0"}, {"value1"}, {"value2"}}, 2, true);
- auto start = std::chrono::steady_clock::now();
- EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 200ms));
- std::chrono::milliseconds expire_time = t.Update(true);
- // Add request @ value1
- EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Add request @ value0 higher prio than value1
- EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 2000ms));
- expire_time = t.Update(true);
- // Retry in 500 ms
- EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
-}
-
-// Test add request
-TEST(FileNodeTest, AddRequestTest) {
- TemporaryFile tf;
- FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 2, true);
- auto start = std::chrono::steady_clock::now();
- EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms));
- std::chrono::milliseconds expire_time = t.Update(true);
- // Add request @ value1
- _VerifyPathValue(tf.path, "value1");
- EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Add request @ value0 higher prio than value1
- EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 200ms));
- expire_time = t.Update(true);
- _VerifyPathValue(tf.path, "value0");
- EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Let high prio request timeout, now only request @ value1 active
- std::this_thread::sleep_for(expire_time + kSLEEP_TOLERANCE_MS);
- expire_time = t.Update(true);
- _VerifyPathValue(tf.path, "value1");
- EXPECT_NEAR(std::chrono::milliseconds(300).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Let all requests timeout, now default value2
- std::this_thread::sleep_for(expire_time + kSLEEP_TOLERANCE_MS);
- expire_time = t.Update(true);
- _VerifyPathValue(tf.path, "value2");
- EXPECT_EQ(std::chrono::milliseconds::max(), expire_time);
-}
-
-// Test remove request
-TEST(FileNodeTest, RemoveRequestTest) {
- TemporaryFile tf;
- FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 2, true);
- auto start = std::chrono::steady_clock::now();
- EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms));
- std::chrono::milliseconds expire_time = t.Update(true);
- // Add request @ value1
- _VerifyPathValue(tf.path, "value1");
- EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Add request @ value0 higher prio than value1
- EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 200ms));
- expire_time = t.Update(true);
- _VerifyPathValue(tf.path, "value0");
- EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Remove high prio request, now only request @ value1 active
- t.RemoveRequest("LAUNCH");
- expire_time = t.Update(true);
- _VerifyPathValue(tf.path, "value1");
- EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Remove request, now default value2
- t.RemoveRequest("INTERACTION");
- expire_time = t.Update(true);
- _VerifyPathValue(tf.path, "value2");
- EXPECT_EQ(std::chrono::milliseconds::max(), expire_time);
-}
-
-// Test add request with holding fd
-TEST(FileNodeTest, AddRequestTestHoldFdOverride) {
- TemporaryFile tf;
- FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 2, true,
- true);
- EXPECT_TRUE(t.GetHoldFd());
- auto start = std::chrono::steady_clock::now();
- EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms));
- std::chrono::milliseconds expire_time = t.Update(true);
- // Add request @ value1
- _VerifyPathValue(tf.path, "value1");
- EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Add request @ value0 higher prio than value1
- EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 200ms));
- expire_time = t.Update(true);
- _VerifyPathValue(tf.path, "value0");
- EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Add request @ value0 shorter
- EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 100ms));
- expire_time = t.Update(true);
- _VerifyPathValue(tf.path, "value0");
- EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Add request @ value0 longer
- EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 300ms));
- expire_time = t.Update(true);
- _VerifyPathValue(tf.path, "value0");
- EXPECT_NEAR(std::chrono::milliseconds(300).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Remove high prio request, now only request @ value1 active
- t.RemoveRequest("LAUNCH");
- expire_time = t.Update(true);
- _VerifyPathValue(tf.path, "value1");
- EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Remove request, now default value2
- t.RemoveRequest("INTERACTION");
- expire_time = t.Update(true);
- _VerifyPathValue(tf.path, "value2");
- EXPECT_EQ(std::chrono::milliseconds::max(), expire_time);
-}
-
-} // namespace perfmgr
-} // namespace android
diff --git a/libperfmgr/tests/HintManagerTest.cc b/libperfmgr/tests/HintManagerTest.cc
deleted file mode 100644
index 3df1fb54..00000000
--- a/libperfmgr/tests/HintManagerTest.cc
+++ /dev/null
@@ -1,528 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specic language governing permissions and
- * limitations under the License.
- */
-
-#include <algorithm>
-#include <thread>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-
-#include <gtest/gtest.h>
-
-#include "perfmgr/FileNode.h"
-#include "perfmgr/HintManager.h"
-#include "perfmgr/PropertyNode.h"
-
-namespace android {
-namespace perfmgr {
-
-using namespace std::chrono_literals;
-
-constexpr auto kSLEEP_TOLERANCE_MS = 50ms;
-
-// JSON_CONFIG
-// {
-// "Nodes": [
-// {
-// "Name": "CPUCluster0MinFreq",
-// "Path": "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq",
-// "Values": [
-// "1512000",
-// "1134000",
-// "384000"
-// ],
-// "DefaultIndex": 2,
-// "ResetOnInit": true
-// },
-// {
-// "Name": "CPUCluster1MinFreq",
-// "Path": "/sys/devices/system/cpu/cpu4/cpufreq/scaling_min_freq",
-// "Values": [
-// "1512000",
-// "1134000",
-// "384000"
-// ],
-// "HoldFd": true
-// },
-// {
-// "Name": "ModeProperty",
-// "Path": "vendor.pwhal.mode",
-// "Values": [
-// "HIGH",
-// "LOW",
-// "NONE"
-// ],
-// "Type": "Property"
-// }
-// ],
-// "Actions": [
-// {
-// "PowerHint": "INTERACTION",
-// "Node": "CPUCluster1MinFreq",
-// "Value": "1134000",
-// "Duration": 800
-// },
-// {
-// "PowerHint": "INTERACTION",
-// "Node": "ModeProperty",
-// "Value": "LOW",
-// "Duration": 800
-// },
-// {
-// "PowerHint": "LAUNCH",
-// "Node": "CPUCluster0MinFreq",
-// "Value": "1134000",
-// "Duration": 500
-// },
-// {
-// "PowerHint": "LAUNCH",
-// "Node": "ModeProperty",
-// "Value": "HIGH",
-// "Duration": 500
-// },
-// {
-// "PowerHint": "LAUNCH",
-// "Node": "CPUCluster1MinFreq",
-// "Value": "1512000",
-// "Duration": 2000
-// }
-// ]
-// }
-constexpr char kJSON_RAW[] =
- "{\"Nodes\":[{\"Name\":\"CPUCluster0MinFreq\",\"Path\":\"/sys/devices/"
- "system/cpu/cpu0/cpufreq/"
- "scaling_min_freq\",\"Values\":[\"1512000\",\"1134000\",\"384000\"],"
- "\"DefaultIndex\":2,\"ResetOnInit\":true},{\"Name\":\"CPUCluster1MinFreq\","
- "\"Path\":\"/sys/devices/system/cpu/cpu4/cpufreq/"
- "scaling_min_freq\",\"Values\":[\"1512000\",\"1134000\",\"384000\"],"
- "\"HoldFd\":true},{\"Name\":\"ModeProperty\",\"Path\":\"vendor.pwhal."
- "mode\",\"Values\":[\"HIGH\",\"LOW\",\"NONE\"],\"Type\":\"Property\"}],"
- "\"Actions\":[{\"PowerHint\":\"INTERACTION\",\"Node\":"
- "\"CPUCluster1MinFreq\",\"Value\":\"1134000\",\"Duration\":800},{"
- "\"PowerHint\":\"INTERACTION\",\"Node\":\"ModeProperty\",\"Value\":\"LOW\","
- "\"Duration\":800},{\"PowerHint\":\"LAUNCH\",\"Node\":"
- "\"CPUCluster0MinFreq\",\"Value\":\"1134000\",\"Duration\":500},{"
- "\"PowerHint\":\"LAUNCH\",\"Node\":\"ModeProperty\",\"Value\":\"HIGH\","
- "\"Duration\":500},{\"PowerHint\":\"LAUNCH\",\"Node\":"
- "\"CPUCluster1MinFreq\",\"Value\":\"1512000\",\"Duration\":2000}]}";
-
-class HintManagerTest : public ::testing::Test, public HintManager {
- protected:
- HintManagerTest()
- : HintManager(nullptr,
- std::map<std::string, std::vector<NodeAction>>{}) {
- android::base::SetMinimumLogSeverity(android::base::VERBOSE);
- prop_ = "vendor.pwhal.mode";
- }
-
- virtual void SetUp() {
- // Set up 3 dummy nodes
- std::unique_ptr<TemporaryFile> tf = std::make_unique<TemporaryFile>();
- nodes_.emplace_back(new FileNode(
- "n0", tf->path, {{"n0_value0"}, {"n0_value1"}, {"n0_value2"}}, 2,
- false));
- files_.emplace_back(std::move(tf));
- tf = std::make_unique<TemporaryFile>();
- nodes_.emplace_back(new FileNode(
- "n1", tf->path, {{"n1_value0"}, {"n1_value1"}, {"n1_value2"}}, 2,
- true));
- files_.emplace_back(std::move(tf));
- nodes_.emplace_back(new PropertyNode(
- "n2", prop_, {{"n2_value0"}, {"n2_value1"}, {"n2_value2"}}, 2,
- true));
- nm_ = new NodeLooperThread(std::move(nodes_));
- // Set up dummy actions
- // "INTERACTION"
- // Node0, value1, 800ms
- // Node1, value1, forever
- // Node2, value1, 800ms
- // "LAUNCH"
- // Node0, value0, forever
- // Node1, value0, 400ms
- // Node2, value0, 400ms
- actions_ = std::map<std::string, std::vector<NodeAction>>{
- {"INTERACTION", {{0, 1, 800ms}, {1, 1, 0ms}, {2, 1, 800ms}}},
- {"LAUNCH", {{0, 0, 0ms}, {1, 0, 400ms}, {2, 0, 400ms}}}};
-
- // Prepare dummy files to replace the nodes' path in example json_doc
- files_.emplace_back(std::make_unique<TemporaryFile>());
- files_.emplace_back(std::make_unique<TemporaryFile>());
- // replace file path
- json_doc_ = kJSON_RAW;
- std::string from =
- "/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq";
- size_t start_pos = json_doc_.find(from);
- json_doc_.replace(start_pos, from.length(), files_[0 + 2]->path);
- from = "/sys/devices/system/cpu/cpu4/cpufreq/scaling_min_freq";
- start_pos = json_doc_.find(from);
- json_doc_.replace(start_pos, from.length(), files_[1 + 2]->path);
- EXPECT_TRUE(android::base::SetProperty(prop_, ""))
- << "failed to clear property";
- }
-
- virtual void TearDown() {
- actions_.clear();
- nodes_.clear();
- files_.clear();
- nm_ = nullptr;
- }
- sp<NodeLooperThread> nm_;
- std::map<std::string, std::vector<NodeAction>> actions_;
- std::vector<std::unique_ptr<Node>> nodes_;
- std::vector<std::unique_ptr<TemporaryFile>> files_;
- std::string json_doc_;
- std::string prop_;
-};
-
-static inline void _VerifyPropertyValue(const std::string& path,
- const std::string& value) {
- std::string s = android::base::GetProperty(path, "");
- EXPECT_EQ(value, s);
-}
-
-static inline void _VerifyPathValue(const std::string& path,
- const std::string& value) {
- std::string s;
- EXPECT_TRUE(android::base::ReadFileToString(path, &s)) << strerror(errno);
- EXPECT_EQ(value, s);
-}
-
-// Test GetHints
-TEST_F(HintManagerTest, GetHintsTest) {
- HintManager hm(nm_, actions_);
- std::vector<std::string> hints = hm.GetHints();
- EXPECT_TRUE(hm.IsRunning());
- EXPECT_EQ(2u, hints.size());
- EXPECT_NE(std::find(hints.begin(), hints.end(), "INTERACTION"), hints.end());
- EXPECT_NE(std::find(hints.begin(), hints.end(), "LAUNCH"), hints.end());
-}
-
-// Test initialization of default values
-TEST_F(HintManagerTest, HintInitDefaultTest) {
- HintManager hm(nm_, actions_);
- std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
- EXPECT_TRUE(hm.IsRunning());
- _VerifyPathValue(files_[0]->path, "");
- _VerifyPathValue(files_[1]->path, "n1_value2");
- _VerifyPropertyValue(prop_, "");
-}
-
-// Test hint/cancel/expire with dummy actions
-TEST_F(HintManagerTest, HintTest) {
- HintManager hm(nm_, actions_);
- EXPECT_TRUE(hm.IsRunning());
- EXPECT_TRUE(hm.DoHint("INTERACTION"));
- std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
- _VerifyPathValue(files_[0]->path, "n0_value1");
- _VerifyPathValue(files_[1]->path, "n1_value1");
- _VerifyPropertyValue(prop_, "n2_value1");
- // this won't change the expire time of INTERACTION hint
- EXPECT_TRUE(hm.DoHint("INTERACTION", 200ms));
- // now place new hint
- EXPECT_TRUE(hm.DoHint("LAUNCH"));
- std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
- _VerifyPathValue(files_[0]->path, "n0_value0");
- _VerifyPathValue(files_[1]->path, "n1_value0");
- _VerifyPropertyValue(prop_, "n2_value0");
- EXPECT_TRUE(hm.DoHint("LAUNCH", 500ms));
- // "LAUNCH" node1 not expired
- std::this_thread::sleep_for(400ms);
- _VerifyPathValue(files_[0]->path, "n0_value0");
- _VerifyPathValue(files_[1]->path, "n1_value0");
- _VerifyPropertyValue(prop_, "n2_value0");
- // "LAUNCH" node1 expired
- std::this_thread::sleep_for(100ms + kSLEEP_TOLERANCE_MS);
- _VerifyPathValue(files_[0]->path, "n0_value0");
- _VerifyPathValue(files_[1]->path, "n1_value1");
- _VerifyPropertyValue(prop_, "n2_value1");
- EXPECT_TRUE(hm.EndHint("LAUNCH"));
- std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
- // "LAUNCH" canceled
- _VerifyPathValue(files_[0]->path, "n0_value1");
- _VerifyPathValue(files_[1]->path, "n1_value1");
- _VerifyPropertyValue(prop_, "n2_value1");
- std::this_thread::sleep_for(200ms);
- // "INTERACTION" node0 expired
- _VerifyPathValue(files_[0]->path, "n0_value2");
- _VerifyPathValue(files_[1]->path, "n1_value1");
- _VerifyPropertyValue(prop_, "n2_value2");
- EXPECT_TRUE(hm.EndHint("INTERACTION"));
- std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
- // "INTERACTION" canceled
- _VerifyPathValue(files_[0]->path, "n0_value2");
- _VerifyPathValue(files_[1]->path, "n1_value2");
- _VerifyPropertyValue(prop_, "n2_value2");
-}
-
-// Test parsing nodes
-TEST_F(HintManagerTest, ParseNodesTest) {
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
- EXPECT_EQ(3u, nodes.size());
- EXPECT_EQ("CPUCluster0MinFreq", nodes[0]->GetName());
- EXPECT_EQ("CPUCluster1MinFreq", nodes[1]->GetName());
- EXPECT_EQ(files_[0 + 2]->path, nodes[0]->GetPath());
- EXPECT_EQ(files_[1 + 2]->path, nodes[1]->GetPath());
- EXPECT_EQ("1512000", nodes[0]->GetValues()[0]);
- EXPECT_EQ("1134000", nodes[0]->GetValues()[1]);
- EXPECT_EQ("384000", nodes[0]->GetValues()[2]);
- EXPECT_EQ("1512000", nodes[1]->GetValues()[0]);
- EXPECT_EQ("1134000", nodes[1]->GetValues()[1]);
- EXPECT_EQ("384000", nodes[1]->GetValues()[2]);
- EXPECT_EQ(2u, nodes[0]->GetDefaultIndex());
- EXPECT_EQ(2u, nodes[1]->GetDefaultIndex());
- EXPECT_TRUE(nodes[0]->GetResetOnInit());
- EXPECT_FALSE(nodes[1]->GetResetOnInit());
- // no dynamic_cast intentionally in Android
- EXPECT_FALSE(reinterpret_cast<FileNode*>(nodes[0].get())->GetHoldFd());
- EXPECT_TRUE(reinterpret_cast<FileNode*>(nodes[1].get())->GetHoldFd());
- EXPECT_EQ("ModeProperty", nodes[2]->GetName());
- EXPECT_EQ(prop_, nodes[2]->GetPath());
- EXPECT_EQ("HIGH", nodes[2]->GetValues()[0]);
- EXPECT_EQ("LOW", nodes[2]->GetValues()[1]);
- EXPECT_EQ("NONE", nodes[2]->GetValues()[2]);
- EXPECT_EQ(2u, nodes[2]->GetDefaultIndex());
- EXPECT_FALSE(nodes[2]->GetResetOnInit());
-}
-
-// Test parsing nodes with duplicate name
-TEST_F(HintManagerTest, ParseNodesDuplicateNameTest) {
- std::string from = "CPUCluster0MinFreq";
- size_t start_pos = json_doc_.find(from);
- json_doc_.replace(start_pos, from.length(), "CPUCluster1MinFreq");
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
- EXPECT_EQ(0u, nodes.size());
-}
-
-TEST_F(HintManagerTest, ParsePropertyNodesDuplicatNameTest) {
- std::string from = "ModeProperty";
- size_t start_pos = json_doc_.find(from);
- json_doc_.replace(start_pos, from.length(), "CPUCluster1MinFreq");
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
- EXPECT_EQ(0u, nodes.size());
-}
-
-// Test parsing nodes with duplicate path
-TEST_F(HintManagerTest, ParseNodesDuplicatePathTest) {
- std::string from = files_[0 + 2]->path;
- size_t start_pos = json_doc_.find(from);
- json_doc_.replace(start_pos, from.length(), files_[1 + 2]->path);
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
- EXPECT_EQ(0u, nodes.size());
-}
-
-// Test parsing file node with duplicate value
-TEST_F(HintManagerTest, ParseFileNodesDuplicateValueTest) {
- std::string from = "1512000";
- size_t start_pos = json_doc_.find(from);
- json_doc_.replace(start_pos, from.length(), "1134000");
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
- EXPECT_EQ(0u, nodes.size());
-}
-
-// Test parsing property node with duplicate value
-TEST_F(HintManagerTest, ParsePropertyNodesDuplicateValueTest) {
- std::string from = "HIGH";
- size_t start_pos = json_doc_.find(from);
- json_doc_.replace(start_pos, from.length(), "LOW");
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
- EXPECT_EQ(0u, nodes.size());
-}
-
-// Test parsing file node with empty value
-TEST_F(HintManagerTest, ParseFileNodesEmptyValueTest) {
- std::string from = "384000";
- size_t start_pos = json_doc_.find(from);
- json_doc_.replace(start_pos, from.length(), "");
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
- EXPECT_EQ(0u, nodes.size());
-}
-
-// Test parsing property node with empty value
-TEST_F(HintManagerTest, ParsePropertyNodesEmptyValueTest) {
- std::string from = "LOW";
- size_t start_pos = json_doc_.find(from);
- json_doc_.replace(start_pos, from.length(), "");
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
- EXPECT_EQ(3u, nodes.size());
- EXPECT_EQ("CPUCluster0MinFreq", nodes[0]->GetName());
- EXPECT_EQ("CPUCluster1MinFreq", nodes[1]->GetName());
- EXPECT_EQ(files_[0 + 2]->path, nodes[0]->GetPath());
- EXPECT_EQ(files_[1 + 2]->path, nodes[1]->GetPath());
- EXPECT_EQ("1512000", nodes[0]->GetValues()[0]);
- EXPECT_EQ("1134000", nodes[0]->GetValues()[1]);
- EXPECT_EQ("384000", nodes[0]->GetValues()[2]);
- EXPECT_EQ("1512000", nodes[1]->GetValues()[0]);
- EXPECT_EQ("1134000", nodes[1]->GetValues()[1]);
- EXPECT_EQ("384000", nodes[1]->GetValues()[2]);
- EXPECT_EQ(2u, nodes[0]->GetDefaultIndex());
- EXPECT_EQ(2u, nodes[1]->GetDefaultIndex());
- EXPECT_TRUE(nodes[0]->GetResetOnInit());
- EXPECT_FALSE(nodes[1]->GetResetOnInit());
- // no dynamic_cast intentionally in Android
- EXPECT_FALSE(reinterpret_cast<FileNode*>(nodes[0].get())->GetHoldFd());
- EXPECT_TRUE(reinterpret_cast<FileNode*>(nodes[1].get())->GetHoldFd());
- EXPECT_EQ("ModeProperty", nodes[2]->GetName());
- EXPECT_EQ(prop_, nodes[2]->GetPath());
- EXPECT_EQ("HIGH", nodes[2]->GetValues()[0]);
- EXPECT_EQ("", nodes[2]->GetValues()[1]);
- EXPECT_EQ("NONE", nodes[2]->GetValues()[2]);
- EXPECT_EQ(2u, nodes[2]->GetDefaultIndex());
- EXPECT_FALSE(nodes[2]->GetResetOnInit());
-}
-
-// Test parsing invalid json for nodes
-TEST_F(HintManagerTest, ParseBadFileNodesTest) {
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes("invalid json");
- EXPECT_EQ(0u, nodes.size());
- nodes = HintManager::ParseNodes(
- "{\"devices\":{\"15\":[\"armeabi-v7a\"],\"16\":[\"armeabi-v7a\"],"
- "\"26\":[\"armeabi-v7a\",\"arm64-v8a\",\"x86\",\"x86_64\"]}}");
- EXPECT_EQ(0u, nodes.size());
-}
-
-// Test parsing actions
-TEST_F(HintManagerTest, ParseActionsTest) {
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
- std::map<std::string, std::vector<NodeAction>> actions =
- HintManager::ParseActions(json_doc_, nodes);
- EXPECT_EQ(2u, actions.size());
-
- EXPECT_EQ(2u, actions["INTERACTION"].size());
- EXPECT_EQ(1u, actions["INTERACTION"][0].node_index);
- EXPECT_EQ(1u, actions["INTERACTION"][0].value_index);
- EXPECT_EQ(std::chrono::milliseconds(800).count(),
- actions["INTERACTION"][0].timeout_ms.count());
-
- EXPECT_EQ(2u, actions["INTERACTION"][1].node_index);
- EXPECT_EQ(1u, actions["INTERACTION"][1].value_index);
- EXPECT_EQ(std::chrono::milliseconds(800).count(),
- actions["INTERACTION"][1].timeout_ms.count());
-
- EXPECT_EQ(3u, actions["LAUNCH"].size());
-
- EXPECT_EQ(0u, actions["LAUNCH"][0].node_index);
- EXPECT_EQ(1u, actions["LAUNCH"][0].value_index);
- EXPECT_EQ(std::chrono::milliseconds(500).count(),
- actions["LAUNCH"][0].timeout_ms.count());
-
- EXPECT_EQ(2u, actions["LAUNCH"][1].node_index);
- EXPECT_EQ(0u, actions["LAUNCH"][1].value_index);
- EXPECT_EQ(std::chrono::milliseconds(500).count(),
- actions["LAUNCH"][1].timeout_ms.count());
-
- EXPECT_EQ(1u, actions["LAUNCH"][2].node_index);
- EXPECT_EQ(0u, actions["LAUNCH"][2].value_index);
- EXPECT_EQ(std::chrono::milliseconds(2000).count(),
- actions["LAUNCH"][2].timeout_ms.count());
-}
-
-// Test parsing actions with duplicate File node
-TEST_F(HintManagerTest, ParseActionDuplicateFileNodeTest) {
- std::string from = "\"Node\":\"CPUCluster0MinFreq\"";
- size_t start_pos = json_doc_.find(from);
- json_doc_.replace(start_pos, from.length(),
- "\"Node\":\"CPUCluster1MinFreq\"");
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
- EXPECT_EQ(3u, nodes.size());
- std::map<std::string, std::vector<NodeAction>> actions =
- HintManager::ParseActions(json_doc_, nodes);
- EXPECT_EQ(0u, actions.size());
-}
-
-// Test parsing actions with duplicate Property node
-TEST_F(HintManagerTest, ParseActionDuplicatePropertyNodeTest) {
- std::string from = "\"Node\":\"CPUCluster0MinFreq\"";
- size_t start_pos = json_doc_.find(from);
- json_doc_.replace(start_pos, from.length(), "\"Node\":\"ModeProperty\"");
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
- EXPECT_EQ(3u, nodes.size());
- std::map<std::string, std::vector<NodeAction>> actions =
- HintManager::ParseActions(json_doc_, nodes);
- EXPECT_EQ(0u, actions.size());
-}
-
-// Test parsing invalid json for actions
-TEST_F(HintManagerTest, ParseBadActionsTest) {
- std::vector<std::unique_ptr<Node>> nodes =
- HintManager::ParseNodes(json_doc_);
- std::map<std::string, std::vector<NodeAction>> actions =
- HintManager::ParseActions("invalid json", nodes);
- EXPECT_EQ(0u, actions.size());
- actions = HintManager::ParseActions(
- "{\"devices\":{\"15\":[\"armeabi-v7a\"],\"16\":[\"armeabi-v7a\"],"
- "\"26\":[\"armeabi-v7a\",\"arm64-v8a\",\"x86\",\"x86_64\"]}}",
- nodes);
- EXPECT_EQ(0u, actions.size());
-}
-
-// Test hint/cancel/expire with json config
-TEST_F(HintManagerTest, GetFromJSONTest) {
- TemporaryFile json_file;
- ASSERT_TRUE(android::base::WriteStringToFile(json_doc_, json_file.path))
- << strerror(errno);
- std::unique_ptr<HintManager> hm = HintManager::GetFromJSON(json_file.path);
- EXPECT_NE(nullptr, hm.get());
- std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
- EXPECT_TRUE(hm->IsRunning());
- // Initial default value on Node0
- _VerifyPathValue(files_[0 + 2]->path, "384000");
- _VerifyPathValue(files_[1 + 2]->path, "");
- _VerifyPropertyValue(prop_, "");
- // Do INTERACTION
- EXPECT_TRUE(hm->DoHint("INTERACTION"));
- std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
- _VerifyPathValue(files_[0 + 2]->path, "384000");
- _VerifyPathValue(files_[1 + 2]->path, "1134000");
- _VerifyPropertyValue(prop_, "LOW");
- // Do LAUNCH
- EXPECT_TRUE(hm->DoHint("LAUNCH"));
- std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
- _VerifyPathValue(files_[0 + 2]->path, "1134000");
- _VerifyPathValue(files_[1 + 2]->path, "1512000");
- _VerifyPropertyValue(prop_, "HIGH");
- std::this_thread::sleep_for(500ms);
- // "LAUNCH" node0 expired
- _VerifyPathValue(files_[0 + 2]->path, "384000");
- _VerifyPathValue(files_[1 + 2]->path, "1512000");
- _VerifyPropertyValue(prop_, "LOW");
- EXPECT_TRUE(hm->EndHint("LAUNCH"));
- std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
- // "LAUNCH" canceled
- _VerifyPathValue(files_[0 + 2]->path, "384000");
- _VerifyPathValue(files_[1 + 2]->path, "1134000");
- _VerifyPropertyValue(prop_, "LOW");
- std::this_thread::sleep_for(300ms);
- // "INTERACTION" node1 expired
- _VerifyPathValue(files_[0 + 2]->path, "384000");
- _VerifyPathValue(files_[1 + 2]->path, "384000");
- _VerifyPropertyValue(prop_, "NONE");
-}
-
-} // namespace perfmgr
-} // namespace android
diff --git a/libperfmgr/tests/NodeLooperThreadTest.cc b/libperfmgr/tests/NodeLooperThreadTest.cc
deleted file mode 100644
index fd634d89..00000000
--- a/libperfmgr/tests/NodeLooperThreadTest.cc
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specic language governing permissions and
- * limitations under the License.
- */
-
-#include <algorithm>
-#include <thread>
-
-#include <android-base/file.h>
-
-#include <gtest/gtest.h>
-
-#include "perfmgr/FileNode.h"
-#include "perfmgr/NodeLooperThread.h"
-
-namespace android {
-namespace perfmgr {
-
-using namespace std::chrono_literals;
-
-constexpr auto kSLEEP_TOLERANCE_MS = 50ms;
-
-class NodeLooperThreadTest : public ::testing::Test {
- protected:
- virtual void SetUp() {
- std::unique_ptr<TemporaryFile> tf = std::make_unique<TemporaryFile>();
- nodes_.emplace_back(new FileNode(
- "n0", tf->path, {{"n0_value0"}, {"n0_value1"}, {"n0_value2"}}, 2,
- false));
- files_.emplace_back(std::move(tf));
- tf = std::make_unique<TemporaryFile>();
- nodes_.emplace_back(new FileNode(
- "n1", tf->path, {{"n1_value0"}, {"n1_value1"}, {"n1_value2"}}, 2,
- true));
- files_.emplace_back(std::move(tf));
- }
-
- virtual void TearDown() {
- nodes_.clear();
- files_.clear();
- }
- std::vector<std::unique_ptr<Node>> nodes_;
- std::vector<std::unique_ptr<TemporaryFile>> files_;
-};
-
-static inline void _VerifyPathValue(const std::string& path,
- const std::string& value) {
- std::string s;
- EXPECT_TRUE(android::base::ReadFileToString(path, &s)) << strerror(errno);
- EXPECT_EQ(value, s);
-}
-
-// Test default value init
-TEST_F(NodeLooperThreadTest, InitRunTest) {
- sp<NodeLooperThread> th = new NodeLooperThread(std::move(nodes_));
- std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
- EXPECT_TRUE(th->isRunning());
- _VerifyPathValue(files_[0]->path, "");
- _VerifyPathValue(files_[1]->path, "n1_value2");
- th->Stop();
- EXPECT_FALSE(th->isRunning());
-}
-
-// Test add request
-TEST_F(NodeLooperThreadTest, AddRequest) {
- sp<NodeLooperThread> th = new NodeLooperThread(std::move(nodes_));
- EXPECT_TRUE(th->isRunning());
- // Dummy LAUNCH boost actions:
- // Node0, value0, 200ms
- // Node1, value1, 400ms
- std::vector<NodeAction> actions{{0, 0, 200ms}, {1, 1, 400ms}};
- EXPECT_TRUE(th->Request(actions, "LAUNCH"));
- std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
- _VerifyPathValue(files_[0]->path, "n0_value0");
- _VerifyPathValue(files_[1]->path, "n1_value1");
- std::this_thread::sleep_for(200ms);
- _VerifyPathValue(files_[0]->path, "n0_value2");
- _VerifyPathValue(files_[1]->path, "n1_value1");
- std::this_thread::sleep_for(200ms);
- _VerifyPathValue(files_[0]->path, "n0_value2");
- _VerifyPathValue(files_[1]->path, "n1_value2");
- th->Stop();
- EXPECT_FALSE(th->isRunning());
-}
-
-// Test request to override expire time
-TEST_F(NodeLooperThreadTest, AddRequestOverride) {
- sp<NodeLooperThread> th = new NodeLooperThread(std::move(nodes_));
- EXPECT_TRUE(th->isRunning());
- // Dummy LAUNCH boost actions:
- // Node0, value0, 200ms
- // Node1, value1, 500ms
- std::vector<NodeAction> actions{{0, 0, 200ms}, {1, 1, 500ms}};
- EXPECT_TRUE(th->Request(actions, "LAUNCH"));
- std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
- _VerifyPathValue(files_[0]->path, "n0_value0");
- _VerifyPathValue(files_[1]->path, "n1_value1");
- // Dummy LAUNCH boost actions:
- // Node0, value0, 300ms will extend
- // Node1, value1, 100ms will not extend
- actions = std::vector<NodeAction>{{0, 0, 300ms}, {1, 1, 100ms}};
- EXPECT_TRUE(th->Request(actions, "LAUNCH"));
- std::this_thread::sleep_for(200ms);
- _VerifyPathValue(files_[0]->path, "n0_value0");
- _VerifyPathValue(files_[1]->path, "n1_value1");
- std::this_thread::sleep_for(150ms);
- // Node0 value0 expired
- _VerifyPathValue(files_[0]->path, "n0_value2");
- _VerifyPathValue(files_[1]->path, "n1_value1");
- std::this_thread::sleep_for(150ms);
- _VerifyPathValue(files_[0]->path, "n0_value2");
- _VerifyPathValue(files_[1]->path, "n1_value2");
- th->Stop();
- EXPECT_FALSE(th->isRunning());
-}
-
-// Test cancel request
-TEST_F(NodeLooperThreadTest, CancelRequest) {
- sp<NodeLooperThread> th = new NodeLooperThread(std::move(nodes_));
- EXPECT_TRUE(th->isRunning());
- // Dummy LAUNCH boost actions:
- // Node0, value0, forever
- // Node1, value1, forever
- std::vector<NodeAction> actions{{0, 0, 0ms}, {1, 1, 0ms}};
- EXPECT_TRUE(th->Request(actions, "LAUNCH"));
- std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
- _VerifyPathValue(files_[0]->path, "n0_value0");
- _VerifyPathValue(files_[1]->path, "n1_value1");
- EXPECT_TRUE(th->Cancel(actions, "LAUNCH"));
- std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
- _VerifyPathValue(files_[0]->path, "n0_value2");
- _VerifyPathValue(files_[1]->path, "n1_value2");
- th->Stop();
- EXPECT_FALSE(th->isRunning());
-}
-
-// Test multiple request
-TEST_F(NodeLooperThreadTest, MultipleRequest) {
- sp<NodeLooperThread> th = new NodeLooperThread(std::move(nodes_));
- EXPECT_TRUE(th->isRunning());
- // Dummy LAUNCH boost actions:
- // Node0, value1, 800ms
- // Node1, value1, forever
- std::vector<NodeAction> actions_interaction{{0, 1, 800ms}, {1, 1, 0ms}};
- EXPECT_TRUE(th->Request(actions_interaction, "INTERACTION"));
- std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
- _VerifyPathValue(files_[0]->path, "n0_value1");
- _VerifyPathValue(files_[1]->path, "n1_value1");
- // Dummy LAUNCH boost actions:
- // Node0, value0, forever
- // Node1, value0, 400ms
- std::vector<NodeAction> actions_launch{{0, 0, 0ms}, {1, 0, 400ms}};
- EXPECT_TRUE(th->Request(actions_launch, "LAUNCH"));
- std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
- _VerifyPathValue(files_[0]->path, "n0_value0");
- _VerifyPathValue(files_[1]->path, "n1_value0");
- std::this_thread::sleep_for(400ms);
- // "LAUNCH" node1 expired
- _VerifyPathValue(files_[0]->path, "n0_value0");
- _VerifyPathValue(files_[1]->path, "n1_value1");
- EXPECT_TRUE(th->Cancel(actions_launch, "LAUNCH"));
- std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
- // "LAUNCH" canceled
- _VerifyPathValue(files_[0]->path, "n0_value1");
- _VerifyPathValue(files_[1]->path, "n1_value1");
- std::this_thread::sleep_for(400ms);
- // "INTERACTION" node0 expired
- _VerifyPathValue(files_[0]->path, "n0_value2");
- _VerifyPathValue(files_[1]->path, "n1_value1");
- EXPECT_TRUE(th->Cancel(actions_interaction, "INTERACTION"));
- std::this_thread::sleep_for(kSLEEP_TOLERANCE_MS);
- // "INTERACTION" canceled
- _VerifyPathValue(files_[0]->path, "n0_value2");
- _VerifyPathValue(files_[1]->path, "n1_value2");
- th->Stop();
- EXPECT_FALSE(th->isRunning());
-}
-
-} // namespace perfmgr
-} // namespace android
diff --git a/libperfmgr/tests/PropertyNodeTest.cc b/libperfmgr/tests/PropertyNodeTest.cc
deleted file mode 100644
index 1d1e332b..00000000
--- a/libperfmgr/tests/PropertyNodeTest.cc
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specic language governing permissions and
- * limitations under the License.
- */
-
-#include <algorithm>
-#include <thread>
-
-#include <android-base/file.h>
-#include <android-base/properties.h>
-#include <android-base/stringprintf.h>
-
-#include <gtest/gtest.h>
-
-#include "perfmgr/PropertyNode.h"
-
-namespace android {
-namespace perfmgr {
-
-using namespace std::chrono_literals;
-
-constexpr double kTIMING_TOLERANCE_MS = std::chrono::milliseconds(25).count();
-constexpr auto kSLEEP_TOLERANCE_MS = 2ms;
-
-static inline void _VerifyPropertyValue(const std::string& path,
- const std::string& value) {
- std::string s = android::base::GetProperty(path, "");
- EXPECT_EQ(value, s);
-}
-
-static inline const std::string _InitProperty(const std::string& path) {
- EXPECT_TRUE(android::base::SetProperty(path, ""))
- << "failed to clear property";
- return path;
-}
-
-// Test init with no default value
-TEST(PropertyNodeTest, NoInitDefaultTest) {
- std::string key = _InitProperty("test.libperfmgr.key");
- PropertyNode t("t", key, {{"value0"}, {"value1"}, {"value2"}}, 1, false);
- _VerifyPropertyValue(key, "");
-}
-
-// Test init with default value
-TEST(PropertyNodeTest, InitDefaultTest) {
- std::string key = _InitProperty("test.libperfmgr.key");
- PropertyNode t("t", key, {{"value0"}, {"value1"}, {"value2"}}, 1, true);
- _VerifyPropertyValue(key, "value1");
- std::string key2 = _InitProperty("test.libperfmgr.key2");
- PropertyNode t2("t2", key2, {{"value0"}, {"value1"}, {"value2"}}, 0, true);
- _VerifyPropertyValue(key2, "value0");
-}
-
-// Test DumpToFd
-TEST(PropertyNodeTest, DumpToFdTest) {
- std::string key = _InitProperty("test.libperfmgr.key");
- PropertyNode t("test_dump", key, {{"value0"}, {"value1"}, {"value2"}}, 1,
- true);
- TemporaryFile dumptf;
- t.DumpToFd(dumptf.fd);
- fsync(dumptf.fd);
- std::string buf(
- android::base::StringPrintf("test_dump\t%s\t1\tvalue1\n", key.c_str()));
- std::string s;
- EXPECT_TRUE(android::base::ReadFileToString(dumptf.path, &s))
- << strerror(errno);
- EXPECT_EQ(buf, s);
-}
-
-// Test GetValueIndex
-TEST(PropertyNodeTest, GetValueIndexTest) {
- std::string key = _InitProperty("test.libperfmgr.key");
- PropertyNode t("t", key, {{"value0"}, {"value1"}, {"value2"}}, 1, false);
- std::size_t index = 0;
- EXPECT_TRUE(t.GetValueIndex("value2", &index));
- EXPECT_EQ(2u, index);
- index = 1234;
- EXPECT_FALSE(t.GetValueIndex("NON_EXIST", &index));
- EXPECT_EQ(1234u, index);
-}
-
-// Test GetValues
-TEST(PropertyNodeTest, GetValuesTest) {
- std::string key = _InitProperty("test.libperfmgr.key");
- PropertyNode t("t", key, {{"value0"}, {"value1"}, {"value2"}}, 1, false);
- std::vector values = t.GetValues();
- EXPECT_EQ(3u, values.size());
- EXPECT_EQ("value0", values[0]);
- EXPECT_EQ("value1", values[1]);
- EXPECT_EQ("value2", values[2]);
-}
-
-// Test get more properties
-TEST(PropertyNodeTest, GetPropertiesTest) {
- std::string test_name = "TESTREQ_1";
- std::string test_path = "TEST_PATH";
- PropertyNode t(test_name, test_path, {}, 0, false);
- EXPECT_EQ(test_name, t.GetName());
- EXPECT_EQ(test_path, t.GetPath());
- EXPECT_EQ(0u, t.GetValues().size());
- EXPECT_EQ(0u, t.GetDefaultIndex());
- EXPECT_FALSE(t.GetResetOnInit());
-}
-
-// Test add request
-TEST(PropertyNodeTest, AddRequestTest) {
- std::string key = _InitProperty("test.libperfmgr.key");
- PropertyNode t("t", key, {{"value0"}, {"value1"}, {""}}, 2, true);
- auto start = std::chrono::steady_clock::now();
- EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms));
- std::chrono::milliseconds expire_time = t.Update(true);
- // Add request @ value1
- _VerifyPropertyValue(key, "value1");
- EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Add request @ value0 higher prio than value1
- EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 200ms));
- expire_time = t.Update(true);
- _VerifyPropertyValue(key, "value0");
- EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Let high prio request timeout, now only request @ value1 active
- std::this_thread::sleep_for(expire_time + kSLEEP_TOLERANCE_MS);
- expire_time = t.Update(true);
- _VerifyPropertyValue(key, "value1");
- EXPECT_NEAR(std::chrono::milliseconds(300).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Let all requests timeout, now default value2
- std::this_thread::sleep_for(expire_time + kSLEEP_TOLERANCE_MS);
- expire_time = t.Update(true);
- _VerifyPropertyValue(key, "");
- EXPECT_EQ(std::chrono::milliseconds::max(), expire_time);
-}
-
-// Test remove request
-TEST(PropertyNodeTest, RemoveRequestTest) {
- std::string key = _InitProperty("test.libperfmgr.key");
- PropertyNode t("t", key, {{"value0"}, {"value1"}, {"value2"}}, 2, true);
- auto start = std::chrono::steady_clock::now();
- EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms));
- std::chrono::milliseconds expire_time = t.Update(true);
- // Add request @ value1
- _VerifyPropertyValue(key, "value1");
- EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Add request @ value0 higher prio than value1
- EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 200ms));
- expire_time = t.Update(true);
- _VerifyPropertyValue(key, "value0");
- EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Remove high prio request, now only request @ value1 active
- t.RemoveRequest("LAUNCH");
- expire_time = t.Update(true);
- _VerifyPropertyValue(key, "value1");
- EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Remove request, now default value2
- t.RemoveRequest("INTERACTION");
- expire_time = t.Update(true);
- _VerifyPropertyValue(key, "value2");
- EXPECT_EQ(std::chrono::milliseconds::max(), expire_time);
-}
-
-// Test add request
-TEST(PropertyNodeTest, AddRequestTestOverride) {
- std::string key = _InitProperty("test.libperfmgr.key");
- PropertyNode t("t", key, {{"value0"}, {"value1"}, {"value2"}}, 2, true);
- auto start = std::chrono::steady_clock::now();
- EXPECT_TRUE(t.AddRequest(1, "INTERACTION", start + 500ms));
- std::chrono::milliseconds expire_time = t.Update(true);
- // Add request @ value1
- _VerifyPropertyValue(key, "value1");
- EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Add request @ value0 higher prio than value1
- EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 200ms));
- expire_time = t.Update(true);
- _VerifyPropertyValue(key, "value0");
- EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Add request @ value0 shorter
- EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 100ms));
- expire_time = t.Update(true);
- _VerifyPropertyValue(key, "value0");
- EXPECT_NEAR(std::chrono::milliseconds(200).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Add request @ value0 longer
- EXPECT_TRUE(t.AddRequest(0, "LAUNCH", start + 300ms));
- expire_time = t.Update(true);
- _VerifyPropertyValue(key, "value0");
- EXPECT_NEAR(std::chrono::milliseconds(300).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Remove high prio request, now only request @ value1 active
- t.RemoveRequest("LAUNCH");
- expire_time = t.Update(true);
- _VerifyPropertyValue(key, "value1");
- EXPECT_NEAR(std::chrono::milliseconds(500).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- // Remove request, now default value2
- t.RemoveRequest("INTERACTION");
- expire_time = t.Update(true);
- _VerifyPropertyValue(key, "value2");
- EXPECT_EQ(std::chrono::milliseconds::max(), expire_time);
-}
-
-} // namespace perfmgr
-} // namespace android
diff --git a/libperfmgr/tests/RequestGroupTest.cc b/libperfmgr/tests/RequestGroupTest.cc
deleted file mode 100644
index 87bfa33c..00000000
--- a/libperfmgr/tests/RequestGroupTest.cc
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specic language governing permissions and
- * limitations under the License.
- */
-
-#include <algorithm>
-#include <thread>
-
-#include <gtest/gtest.h>
-
-#include "perfmgr/RequestGroup.h"
-
-namespace android {
-namespace perfmgr {
-
-using namespace std::chrono_literals;
-
-constexpr double kTIMING_TOLERANCE_MS = std::chrono::milliseconds(25).count();
-
-// Test GetRequestValue()
-TEST(RequestGroupTest, GetRequestValueTest) {
- std::string test_str = "TESTREQ_1";
- RequestGroup req(test_str);
- EXPECT_EQ(test_str, req.GetRequestValue());
-}
-
-// Test AddRequest()
-TEST(RequestGroupTest, AddRequestTest) {
- RequestGroup req("");
- auto start = std::chrono::steady_clock::now();
- auto duration = 500ms;
- bool ret = req.AddRequest("INTERACTION", start + duration);
- EXPECT_EQ(true, ret);
- auto sleep_time = 200ms;
- std::this_thread::sleep_for(sleep_time);
- std::chrono::milliseconds expire_time;
- bool active = req.GetExpireTime(&expire_time);
- EXPECT_NEAR((duration - sleep_time).count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- EXPECT_EQ(true, active);
-}
-
-// Test AddRequest() with a huge expire time which could be done in some long
-// persist power hint such as VR_MODE
-TEST(RequestGroupTest, AddRequestNoExpireTest) {
- RequestGroup req("");
- bool ret = req.AddRequest("INTERACTION", ReqTime::max());
- EXPECT_EQ(true, ret);
- std::chrono::milliseconds expire_time;
- bool active = req.GetExpireTime(&expire_time);
- auto expect = std::chrono::duration_cast<std::chrono::milliseconds>(
- ReqTime::max() - std::chrono::steady_clock::now());
- EXPECT_NEAR(expect.count(), expire_time.count(), kTIMING_TOLERANCE_MS);
- // expire time is greater than 1 year
- EXPECT_LE(365 * 24 * 60 * 60 * 1000, expire_time.count());
- EXPECT_EQ(true, active);
-}
-
-// Test AddRequest() and expires
-TEST(RequestGroupTest, AddRequestTestExpire) {
- RequestGroup req("");
- auto start = std::chrono::steady_clock::now();
- auto duration = 5ms;
- bool ret = req.AddRequest("INTERACTION", start + duration);
- EXPECT_EQ(true, ret);
- ret = req.AddRequest("INTERACTION", start + duration + 1ms);
- EXPECT_EQ(false, ret);
- std::this_thread::sleep_for(duration + 10ms);
- std::chrono::milliseconds expire_time;
- bool active = req.GetExpireTime(&expire_time);
- EXPECT_EQ(std::chrono::milliseconds::max(), expire_time);
- EXPECT_EQ(false, active);
-}
-
-// Test AddRequest() with new value
-TEST(RequestGroupTest, AddRequestNewValue) {
- RequestGroup req("");
- auto start = std::chrono::steady_clock::now();
- auto duration = 5000ms;
- bool ret = req.AddRequest("INTERACTION", start + duration);
- EXPECT_EQ(true, ret);
- std::chrono::milliseconds expire_time;
- bool active = req.GetExpireTime(&expire_time);
- EXPECT_NEAR(duration.count(), expire_time.count(), kTIMING_TOLERANCE_MS);
- EXPECT_EQ(true, active);
- // Add a request shorter than the current outstanding one, expiration time
- // not changed
- auto shorter_duration = 100ms;
- ret = req.AddRequest("INTERACTION", start + shorter_duration);
- EXPECT_EQ(false, ret);
- active = req.GetExpireTime(&expire_time);
- EXPECT_NEAR(duration.count(), expire_time.count(), kTIMING_TOLERANCE_MS);
- EXPECT_EQ(true, active);
- // Add a request longer than the current outstanding one, expiration time
- // changed
- duration = 10000ms;
- ret = req.AddRequest("INTERACTION", start + duration);
- EXPECT_EQ(false, ret);
- active = req.GetExpireTime(&expire_time);
- EXPECT_NEAR(duration.count(), expire_time.count(), kTIMING_TOLERANCE_MS);
- EXPECT_EQ(true, active);
-}
-
-// Test multiple AddRequest() with different hint_type
-TEST(RequestGroupTest, AddRequestTestMutiple) {
- RequestGroup req("");
- auto start = std::chrono::steady_clock::now();
- auto duration_interact = 500ms;
- req.AddRequest("INTERACTION", start + duration_interact);
- auto duration_launch = 5000ms;
- req.AddRequest("LAUNCH", start + duration_launch);
- std::chrono::milliseconds expire_time;
- bool active = req.GetExpireTime(&expire_time);
- EXPECT_NEAR(std::min(duration_interact, duration_launch).count(),
- expire_time.count(), kTIMING_TOLERANCE_MS);
- EXPECT_EQ(true, active);
-}
-
-// Test RemoveRequest()
-TEST(RequestGroupTest, RemoveRequestTest) {
- RequestGroup req("");
- auto start = std::chrono::steady_clock::now();
- auto duration_interact = 500ms;
- req.AddRequest("INTERACTION", start + duration_interact);
- bool ret = req.RemoveRequest("INTERACTION");
- EXPECT_EQ(true, ret);
- std::chrono::milliseconds expire_time;
- bool active = req.GetExpireTime(&expire_time);
- EXPECT_EQ(std::chrono::milliseconds::max(), expire_time);
- EXPECT_EQ(false, active);
- // Test removing an already-removed request
- ret = req.RemoveRequest("INTERACTION");
- EXPECT_EQ(false, ret);
-}
-
-// Test multiple RemoveRequest() with different hint_type
-TEST(RequestGroupTest, RemoveRequestTestMutiple) {
- RequestGroup req("");
- auto start = std::chrono::steady_clock::now();
- auto duration_interact = 500ms;
- req.AddRequest("INTERACTION", start + duration_interact);
- auto duration_launch = 50000ms;
- req.AddRequest("LAUNCH", start + duration_launch);
- req.RemoveRequest("INTERACTION");
- std::chrono::milliseconds expire_time;
- bool active = req.GetExpireTime(&expire_time);
- EXPECT_NEAR(duration_launch.count(), expire_time.count(),
- kTIMING_TOLERANCE_MS);
- EXPECT_EQ(true, active);
-}
-
-} // namespace perfmgr
-} // namespace android
diff --git a/libperfmgr/tools/ConfigVerifier.cc b/libperfmgr/tools/ConfigVerifier.cc
deleted file mode 100644
index 2e25e950..00000000
--- a/libperfmgr/tools/ConfigVerifier.cc
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specic language governing permissions and
- * limitations under the License.
- */
-
-#include <thread>
-
-#include <getopt.h>
-#include <unistd.h>
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <sys/types.h>
-
-#include "perfmgr/HintManager.h"
-
-namespace android {
-namespace perfmgr {
-
-class NodeVerifier : public HintManager {
- public:
- static bool VerifyNodes(const std::string& config_path) {
- std::string json_doc;
-
- if (!android::base::ReadFileToString(config_path, &json_doc)) {
- LOG(ERROR) << "Failed to read JSON config from " << config_path;
- return false;
- }
-
- std::vector<std::unique_ptr<Node>> nodes = ParseNodes(json_doc);
- if (nodes.empty()) {
- LOG(ERROR) << "Failed to parse Nodes section from " << config_path;
- return false;
- }
-
- return true;
- }
-
- private:
- NodeVerifier(sp<NodeLooperThread> nm,
- const std::map<std::string, std::vector<NodeAction>>& actions)
- : HintManager(std::move(nm), actions) {}
-};
-
-} // namespace perfmgr
-} // namespace android
-
-static void printUsage(const char* exec_name) {
- std::string usage = exec_name;
- usage =
- usage +
- " is a command-line tool to verify Nodes in Json config are writable.\n"
- "Usages:\n"
- " [su system] " +
- exec_name +
- " [options]\n"
- "\n"
- "Options:\n"
- " --config, -c [PATH]\n"
- " path to Json config file\n\n"
- " --exec_hint, -e\n"
- " do hints in Json config\n\n"
- " --hint_name, -i\n"
- " do only the specific hint\n\n"
- " --hint_duration, -d [duration]\n"
- " duration in ms for each hint\n\n"
- " --help, -h\n"
- " print this message\n\n"
- " --verbose, -v\n"
- " print verbose log during execution\n\n";
-
- LOG(INFO) << usage;
-}
-
-static void execConfig(const std::string& json_file, const std::string& hint_name, unsigned long hint_duration) {
- std::unique_ptr<android::perfmgr::HintManager> hm =
- android::perfmgr::HintManager::GetFromJSON(json_file);
- if (!hm.get() || !hm->IsRunning()) {
- LOG(ERROR) << "Failed to Parse JSON config";
- }
- std::vector<std::string> hints = hm->GetHints();
- for (const auto& hint : hints) {
- if (!hint_name.empty() && hint_name != hint)
- continue;
- LOG(INFO) << "Do hint: " << hint;
- hm->DoHint(hint, std::chrono::milliseconds(hint_duration));
- std::this_thread::yield();
- std::this_thread::sleep_for(std::chrono::milliseconds(hint_duration));
- LOG(INFO) << "End hint: " << hint;
- hm->EndHint(hint);
- std::this_thread::yield();
- }
-}
-
-int main(int argc, char* argv[]) {
- android::base::InitLogging(argv, android::base::StdioLogger);
-
- if (getuid() == 0) {
- LOG(WARNING) << "Running as root might mask node permission";
- }
-
- std::string config_path;
- std::string hint_name;
- bool exec_hint = false;
- unsigned long hint_duration = 100;
-
- while (true) {
- static struct option opts[] = {
- {"config", required_argument, nullptr, 'c'},
- {"exec_hint", no_argument, nullptr, 'e'},
- {"hint_name", required_argument, nullptr, 'i'},
- {"hint_duration", required_argument, nullptr, 'd'},
- {"help", no_argument, nullptr, 'h'},
- {"verbose", no_argument, nullptr, 'v'},
- {0, 0, 0, 0} // termination of the option list
- };
-
- int option_index = 0;
- int c = getopt_long(argc, argv, "c:ei:d:hv", opts, &option_index);
- if (c == -1) {
- break;
- }
-
- switch (c) {
- case 'c':
- config_path = optarg;
- break;
- case 'e':
- exec_hint = true;
- break;
- case 'i':
- hint_name = optarg;
- break;
- case 'd':
- hint_duration = strtoul(optarg, NULL, 10);
- break;
- case 'v':
- android::base::SetMinimumLogSeverity(android::base::VERBOSE);
- break;
- case 'h':
- printUsage(argv[0]);
- return 0;
- default:
- // getopt already prints "invalid option -- %c" for us.
- return 1;
- }
- }
-
- if (config_path.empty()) {
- LOG(ERROR) << "Need specify JSON config";
- printUsage(argv[0]);
- return 1;
- }
-
- if (exec_hint) {
- execConfig(config_path, hint_name, hint_duration);
- return 0;
- }
-
- if (android::perfmgr::NodeVerifier::VerifyNodes(config_path)) {
- LOG(INFO) << "Verified writing to JSON config";
- return 0;
- } else {
- LOG(ERROR) << "Failed to verify nodes in JSON config";
- return 1;
- }
-}
diff --git a/simpleperf/Android.bp b/simpleperf/Android.bp
index 089a71ad..f1f2d6bc 100644
--- a/simpleperf/Android.bp
+++ b/simpleperf/Android.bp
@@ -166,7 +166,6 @@ cc_defaults {
linux: {
static_libs: [
"libunwindstack",
- "libdexfile_support_static",
"libcutils",
"libprocinfo",
"libevent",
@@ -327,6 +326,13 @@ cc_library_static {
"libbuildversion",
],
use_version_lib: false,
+
+ target: {
+ linux: {
+ // See note for libdexfile_support_static in simpleperf_ndk.
+ static_libs: ["libdexfile_support"],
+ },
+ },
}
// simpleperf shipped in system image
@@ -406,6 +412,14 @@ cc_binary {
dir: "simpleperf/darwin/x86_64",
},
},
+ linux: {
+ // In the NDK we need libdexfile_support_static which links
+ // libdexfile_external and its ART dependencies statically. However
+ // in other libraries we must use libdexfile_support, which dlopen's
+ // libdexfile_external.so from the ART APEX, to avoid getting ART
+ // internals in the system image.
+ static_libs: ["libdexfile_support_static"],
+ },
linux_glibc_x86: {
dist: {
dir: "simpleperf/linux/x86",
@@ -450,6 +464,10 @@ cc_library {
windows: {
enabled: false,
},
+ linux: {
+ // See note for libdexfile_support_static in simpleperf_ndk.
+ static_libs: ["libdexfile_support"],
+ },
},
}
@@ -473,6 +491,9 @@ cc_library_shared {
},
linux: {
ldflags: ["-Wl,--exclude-libs,ALL"],
+ // See note for libdexfile_support_static in simpleperf_ndk. This is
+ // part of the NDK, so use libdexfile_support_static.
+ static_libs: ["libdexfile_support_static"],
},
darwin: {
dist: {
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index ec8c44c9..0879f955 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -872,6 +872,11 @@ TEST(record_cmd, record_java_app) {
TEST(record_cmd, record_native_app) {
#if defined(__ANDROID__)
+ // In case of non-native ABI guest symbols are never directly executed, thus
+ // don't appear in perf.data. Instead binary translator executes code
+ // translated from guest at runtime.
+ OMIT_TEST_ON_NON_NATIVE_ABIS();
+
RecordingAppHelper helper;
// 1. Install apk.
ASSERT_TRUE(helper.InstallApk(GetTestData("EndlessTunnel.apk"), "com.google.sample.tunnel"));
@@ -887,12 +892,10 @@ TEST(record_cmd, record_native_app) {
// 4. Check perf.data.
auto process_symbol = [&](const char* name) {
-#if !defined(IN_CTS_TEST)
const char* expected_name_with_keyguard = "NativeActivity"; // when screen is locked
if (strstr(name, expected_name_with_keyguard) != nullptr) {
return true;
}
-#endif
const char* expected_name = "PlayScene::DoFrame"; // when screen is awake
return strstr(name, expected_name) != nullptr;
};
@@ -1018,4 +1021,4 @@ TEST(record_cmd, exclude_perf_option) {
return true;
}));
}
-} \ No newline at end of file
+}
diff --git a/simpleperf/cmd_stat.cpp b/simpleperf/cmd_stat.cpp
index adc4f0a2..1ee6ef81 100644
--- a/simpleperf/cmd_stat.cpp
+++ b/simpleperf/cmd_stat.cpp
@@ -32,6 +32,7 @@
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
+#include "cmd_stat_impl.h"
#include "command.h"
#include "environment.h"
#include "event_attr.h"
@@ -42,7 +43,9 @@
#include "utils.h"
#include "workload.h"
-namespace {
+using namespace simpleperf;
+
+namespace simpleperf {
static std::vector<std::string> default_measured_event_types{
"cpu-cycles", "stalled-cycles-frontend", "stalled-cycles-backend",
@@ -50,95 +53,6 @@ static std::vector<std::string> default_measured_event_types{
"task-clock", "context-switches", "page-faults",
};
-struct CounterSum {
- uint64_t value = 0;
- uint64_t time_enabled = 0;
- uint64_t time_running = 0;
-};
-
-struct ThreadInfo {
- pid_t tid;
- pid_t pid;
- std::string name;
-};
-
-struct CounterSummary {
- std::string type_name;
- std::string modifier;
- uint32_t group_id;
- const ThreadInfo* thread;
- int cpu; // -1 represents all cpus
- uint64_t count;
- double scale;
- std::string readable_count;
- std::string comment;
- bool auto_generated;
-
- CounterSummary(const std::string& type_name, const std::string& modifier, uint32_t group_id,
- const ThreadInfo* thread, int cpu, uint64_t count, double scale,
- bool auto_generated, bool csv)
- : type_name(type_name),
- modifier(modifier),
- group_id(group_id),
- thread(thread),
- cpu(cpu),
- count(count),
- scale(scale),
- auto_generated(auto_generated) {
- readable_count = ReadableCountValue(csv);
- }
-
- bool IsMonitoredAtTheSameTime(const CounterSummary& other) const {
- // Two summaries are monitored at the same time if they are in the same
- // group or are monitored all the time.
- if (group_id == other.group_id) {
- return true;
- }
- return IsMonitoredAllTheTime() && other.IsMonitoredAllTheTime();
- }
-
- std::string Name() const {
- if (modifier.empty()) {
- return type_name;
- }
- return type_name + ":" + modifier;
- }
-
- bool IsMonitoredAllTheTime() const {
- // If an event runs all the time it is enabled (by not sharing hardware
- // counters with other events), the scale of its summary is usually within
- // [1, 1 + 1e-5]. By setting SCALE_ERROR_LIMIT to 1e-5, We can identify
- // events monitored all the time in most cases while keeping the report
- // error rate <= 1e-5.
- constexpr double SCALE_ERROR_LIMIT = 1e-5;
- return (fabs(scale - 1.0) < SCALE_ERROR_LIMIT);
- }
-
- private:
- std::string ReadableCountValue(bool csv) {
- if (type_name == "cpu-clock" || type_name == "task-clock") {
- // Convert nanoseconds to milliseconds.
- double value = count / 1e6;
- return android::base::StringPrintf("%lf(ms)", value);
- } else {
- // Convert big numbers to human friendly mode. For example,
- // 1000000 will be converted to 1,000,000.
- std::string s = android::base::StringPrintf("%" PRIu64, count);
- if (csv) {
- return s;
- } else {
- for (size_t i = s.size() - 1, j = 1; i > 0; --i, ++j) {
- if (j == 3) {
- s.insert(s.begin() + i, ',');
- j = 0;
- }
- }
- return s;
- }
- }
- }
-};
-
static const std::unordered_map<std::string_view, std::pair<std::string_view, std::string_view>>
COMMON_EVENT_RATE_MAP = {
{"cache-misses", {"cache-references", "miss rate"}},
@@ -176,234 +90,207 @@ static const std::unordered_map<std::string_view, std::pair<std::string_view, st
{"raw-l2d-tlb-refill-rd", {"raw-l2d-tlb-rd", "level 2 data TLB refill rate, read"}},
};
-class CounterSummaries {
- public:
- explicit CounterSummaries(bool csv) : csv_(csv) {}
- std::vector<CounterSummary>& Summaries() { return summaries_; }
-
- const CounterSummary* FindSummary(const std::string& type_name, const std::string& modifier,
- const ThreadInfo* thread, int cpu) {
- for (const auto& s : summaries_) {
- if (s.type_name == type_name && s.modifier == modifier && s.thread == thread &&
- s.cpu == cpu) {
- return &s;
- }
+const CounterSummary* CounterSummaries::FindSummary(const std::string& type_name,
+ const std::string& modifier,
+ const ThreadInfo* thread, int cpu) {
+ for (const auto& s : summaries_) {
+ if (s.type_name == type_name && s.modifier == modifier && s.thread == thread && s.cpu == cpu) {
+ return &s;
}
- return nullptr;
}
+ return nullptr;
+}
- // If we have two summaries monitoring the same event type at the same time,
- // that one is for user space only, and the other is for kernel space only;
- // then we can automatically generate a summary combining the two results.
- // For example, a summary of branch-misses:u and a summary for branch-misses:k
- // can generate a summary of branch-misses.
- void AutoGenerateSummaries() {
- for (size_t i = 0; i < summaries_.size(); ++i) {
- const CounterSummary& s = summaries_[i];
- if (s.modifier == "u") {
- const CounterSummary* other = FindSummary(s.type_name, "k", s.thread, s.cpu);
- if (other != nullptr && other->IsMonitoredAtTheSameTime(s)) {
- if (FindSummary(s.type_name, "", s.thread, s.cpu) == nullptr) {
- Summaries().emplace_back(s.type_name, "", s.group_id, s.thread, s.cpu,
- s.count + other->count, s.scale, true, csv_);
- }
+void CounterSummaries::AutoGenerateSummaries() {
+ for (size_t i = 0; i < summaries_.size(); ++i) {
+ const CounterSummary& s = summaries_[i];
+ if (s.modifier == "u") {
+ const CounterSummary* other = FindSummary(s.type_name, "k", s.thread, s.cpu);
+ if (other != nullptr && other->IsMonitoredAtTheSameTime(s)) {
+ if (FindSummary(s.type_name, "", s.thread, s.cpu) == nullptr) {
+ summaries_.emplace_back(s.type_name, "", s.group_id, s.thread, s.cpu,
+ s.count + other->count, s.runtime_in_ns, s.scale, true, csv_);
}
}
}
}
+}
- void GenerateComments(double duration_in_sec) {
- for (auto& s : summaries_) {
- s.comment = GetCommentForSummary(s, duration_in_sec);
- }
+void CounterSummaries::GenerateComments(double duration_in_sec) {
+ for (auto& s : summaries_) {
+ s.comment = GetCommentForSummary(s, duration_in_sec);
}
+}
- void Show(FILE* fp) {
- if (csv_) {
- ShowCSV(fp);
- } else {
- ShowText(fp);
- }
+void CounterSummaries::Show(FILE* fp) {
+ if (csv_) {
+ ShowCSV(fp);
+ } else {
+ ShowText(fp);
}
+}
- void ShowCSV(FILE* fp) {
- for (auto& s : summaries_) {
- if (s.thread != nullptr) {
- fprintf(fp, "%s,%d,%d,", s.thread->name.c_str(), s.thread->pid, s.thread->tid);
- }
- fprintf(fp, "%s,%s,%s,(%.0lf%%)%s\n", s.readable_count.c_str(), s.Name().c_str(),
- s.comment.c_str(), 1.0 / s.scale * 100, (s.auto_generated ? " (generated)," : ","));
+void CounterSummaries::ShowCSV(FILE* fp) {
+ for (auto& s : summaries_) {
+ if (s.thread != nullptr) {
+ fprintf(fp, "%s,%d,%d,", s.thread->name.c_str(), s.thread->pid, s.thread->tid);
}
+ fprintf(fp, "%s,%s,%s,(%.0f%%)%s\n", s.readable_count.c_str(), s.Name().c_str(),
+ s.comment.c_str(), 1.0 / s.scale * 100, (s.auto_generated ? " (generated)," : ","));
}
+}
- void ShowText(FILE* fp) {
- bool show_thread = !summaries_.empty() && summaries_[0].thread != nullptr;
- bool show_cpu = !summaries_.empty() && summaries_[0].cpu != -1;
- std::vector<std::string> titles;
+void CounterSummaries::ShowText(FILE* fp) {
+ bool show_thread = !summaries_.empty() && summaries_[0].thread != nullptr;
+ bool show_cpu = !summaries_.empty() && summaries_[0].cpu != -1;
+ std::vector<std::string> titles;
- if (show_thread) {
- titles = {"thread_name", "pid", "tid"};
- }
- if (show_cpu) {
- titles.emplace_back("cpu");
- }
- titles.emplace_back("count");
- titles.emplace_back("event_name");
- titles.emplace_back(" # percentage = event_run_time / enabled_time");
+ if (show_thread) {
+ titles = {"thread_name", "pid", "tid"};
+ }
+ if (show_cpu) {
+ titles.emplace_back("cpu");
+ }
+ titles.emplace_back("count");
+ titles.emplace_back("event_name");
+ titles.emplace_back(" # count / runtime, runtime / enabled_time");
- std::vector<size_t> width(titles.size(), 0);
+ std::vector<size_t> width(titles.size(), 0);
- auto adjust_width = [](size_t& w, size_t size) {
- w = std::max(w, size);
- };
+ auto adjust_width = [](size_t& w, size_t size) { w = std::max(w, size); };
- for (size_t i = 0; i < titles.size(); i++) {
- adjust_width(width[i], titles[i].size());
- }
-
- for (auto& s : summaries_) {
- size_t i = 0;
- if (show_thread) {
- adjust_width(width[i++], s.thread->name.size());
- adjust_width(width[i++], std::to_string(s.thread->pid).size());
- adjust_width(width[i++], std::to_string(s.thread->tid).size());
- }
- if (show_cpu) {
- adjust_width(width[i++], std::to_string(s.cpu).size());
- }
- adjust_width(width[i++], s.readable_count.size());
- adjust_width(width[i++], s.Name().size());
- adjust_width(width[i++], s.comment.size());
- }
+ // The last title is too long. Don't include it for width adjustment.
+ for (size_t i = 0; i + 1 < titles.size(); i++) {
+ adjust_width(width[i], titles[i].size());
+ }
- fprintf(fp, "# ");
- for (size_t i = 0; i < titles.size(); i++) {
- if (titles[i] == "count") {
- fprintf(fp, "%*s", static_cast<int>(width[i]), titles[i].c_str());
- } else {
- fprintf(fp, "%-*s", static_cast<int>(width[i]), titles[i].c_str());
- }
- if (i + 1 < titles.size()) {
- fprintf(fp, " ");
- }
+ for (auto& s : summaries_) {
+ size_t i = 0;
+ if (show_thread) {
+ adjust_width(width[i++], s.thread->name.size());
+ adjust_width(width[i++], std::to_string(s.thread->pid).size());
+ adjust_width(width[i++], std::to_string(s.thread->tid).size());
}
- fprintf(fp, "\n");
-
- for (auto& s : summaries_) {
- size_t i = 0;
- if (show_thread) {
- fprintf(fp, " %-*s", static_cast<int>(width[i++]), s.thread->name.c_str());
- fprintf(fp, " %-*d", static_cast<int>(width[i++]), s.thread->pid);
- fprintf(fp, " %-*d", static_cast<int>(width[i++]), s.thread->tid);
- }
- if (show_cpu) {
- fprintf(fp, " %-*d", static_cast<int>(width[i++]), s.cpu);
- }
- fprintf(fp, " %*s %-*s # %-*s (%.0lf%%)%s\n",
- static_cast<int>(width[i]), s.readable_count.c_str(),
- static_cast<int>(width[i+1]), s.Name().c_str(),
- static_cast<int>(width[i+2]), s.comment.c_str(),
- 1.0 / s.scale * 100, (s.auto_generated ? " (generated)" : ""));
+ if (show_cpu) {
+ adjust_width(width[i++], std::to_string(s.cpu).size());
}
+ adjust_width(width[i++], s.readable_count.size());
+ adjust_width(width[i++], s.Name().size());
+ adjust_width(width[i++], s.comment.size());
}
- private:
- std::string GetCommentForSummary(const CounterSummary& s,
- double duration_in_sec) {
- char sap_mid;
- if (csv_) {
- sap_mid = ',';
+ fprintf(fp, "# ");
+ for (size_t i = 0; i < titles.size(); i++) {
+ if (titles[i] == "count") {
+ fprintf(fp, "%*s", static_cast<int>(width[i]), titles[i].c_str());
} else {
- sap_mid = ' ';
- }
- if (s.type_name == "task-clock") {
- double run_sec = s.count / 1e9;
- double used_cpus = run_sec / (duration_in_sec / s.scale);
- return android::base::StringPrintf("%lf%ccpus used", used_cpus, sap_mid);
- }
- if (s.type_name == "cpu-clock") {
- return "";
+ fprintf(fp, "%-*s", static_cast<int>(width[i]), titles[i].c_str());
}
- if (s.type_name == "cpu-cycles") {
- double running_time_in_sec;
- if (!FindRunningTimeForSummary(s, &running_time_in_sec)) {
- return "";
- }
- double hz = s.count / (running_time_in_sec / s.scale);
- return android::base::StringPrintf("%lf%cGHz", hz / 1e9, sap_mid);
+ if (i + 1 < titles.size()) {
+ fprintf(fp, " ");
}
- if (s.type_name == "instructions" && s.count != 0) {
- const CounterSummary* other = FindSummary("cpu-cycles", s.modifier, s.thread, s.cpu);
- if (other != nullptr && other->IsMonitoredAtTheSameTime(s)) {
- double cpi = static_cast<double>(other->count) / s.count;
- return android::base::StringPrintf("%lf%ccycles per instruction", cpi,
- sap_mid);
- }
+ }
+ fprintf(fp, "\n");
+
+ for (auto& s : summaries_) {
+ size_t i = 0;
+ if (show_thread) {
+ fprintf(fp, " %-*s", static_cast<int>(width[i++]), s.thread->name.c_str());
+ fprintf(fp, " %-*d", static_cast<int>(width[i++]), s.thread->pid);
+ fprintf(fp, " %-*d", static_cast<int>(width[i++]), s.thread->tid);
}
- std::string rate_comment = GetRateComment(s, sap_mid);
- if (!rate_comment.empty()) {
- return rate_comment;
+ if (show_cpu) {
+ fprintf(fp, " %-*d", static_cast<int>(width[i++]), s.cpu);
}
- double running_time_in_sec;
- if (!FindRunningTimeForSummary(s, &running_time_in_sec)) {
+ fprintf(fp, " %*s %-*s # %-*s (%.0f%%)%s\n", static_cast<int>(width[i]),
+ s.readable_count.c_str(), static_cast<int>(width[i + 1]), s.Name().c_str(),
+ static_cast<int>(width[i + 2]), s.comment.c_str(), 1.0 / s.scale * 100,
+ (s.auto_generated ? " (generated)" : ""));
+ }
+}
+
+std::string CounterSummaries::GetCommentForSummary(const CounterSummary& s,
+ double duration_in_sec) {
+ char sap_mid;
+ if (csv_) {
+ sap_mid = ',';
+ } else {
+ sap_mid = ' ';
+ }
+ if (s.type_name == "task-clock") {
+ double run_sec = s.count / 1e9;
+ double used_cpus = run_sec / duration_in_sec;
+ return android::base::StringPrintf("%f%ccpus used", used_cpus, sap_mid);
+ }
+ if (s.type_name == "cpu-clock") {
+ return "";
+ }
+ if (s.type_name == "cpu-cycles") {
+ if (s.runtime_in_ns == 0) {
return "";
}
- double rate = s.count / (running_time_in_sec / s.scale);
- if (rate > 1e9) {
- return android::base::StringPrintf("%.3lf%cG/sec", rate / 1e9, sap_mid);
- }
- if (rate > 1e6) {
- return android::base::StringPrintf("%.3lf%cM/sec", rate / 1e6, sap_mid);
- }
- if (rate > 1e3) {
- return android::base::StringPrintf("%.3lf%cK/sec", rate / 1e3, sap_mid);
+ double ghz = static_cast<double>(s.count) / s.runtime_in_ns;
+ return android::base::StringPrintf("%f%cGHz", ghz, sap_mid);
+ }
+ if (s.type_name == "instructions" && s.count != 0) {
+ const CounterSummary* other = FindSummary("cpu-cycles", s.modifier, s.thread, s.cpu);
+ if (other != nullptr && other->IsMonitoredAtTheSameTime(s)) {
+ double cpi = static_cast<double>(other->count) / s.count;
+ return android::base::StringPrintf("%f%ccycles per instruction", cpi, sap_mid);
}
- return android::base::StringPrintf("%.3lf%c/sec", rate, sap_mid);
}
+ std::string rate_comment = GetRateComment(s, sap_mid);
+ if (!rate_comment.empty()) {
+ return rate_comment;
+ }
+ if (s.runtime_in_ns == 0) {
+ return "";
+ }
+ double runtime_in_sec = static_cast<double>(s.runtime_in_ns) / 1e9;
+ double rate = s.count / runtime_in_sec;
+ if (rate >= 1e9 - 1e5) {
+ return android::base::StringPrintf("%.3f%cG/sec", rate / 1e9, sap_mid);
+ }
+ if (rate >= 1e6 - 1e2) {
+ return android::base::StringPrintf("%.3f%cM/sec", rate / 1e6, sap_mid);
+ }
+ if (rate >= 1e3) {
+ return android::base::StringPrintf("%.3f%cK/sec", rate / 1e3, sap_mid);
+ }
+ return android::base::StringPrintf("%.3f%c/sec", rate, sap_mid);
+}
- std::string GetRateComment(const CounterSummary& s, char sep) {
- std::string_view miss_event_name = s.type_name;
- std::string event_name;
- std::string rate_desc;
- if (auto it = COMMON_EVENT_RATE_MAP.find(miss_event_name); it != COMMON_EVENT_RATE_MAP.end()) {
+std::string CounterSummaries::GetRateComment(const CounterSummary& s, char sep) {
+ std::string_view miss_event_name = s.type_name;
+ std::string event_name;
+ std::string rate_desc;
+ if (auto it = COMMON_EVENT_RATE_MAP.find(miss_event_name); it != COMMON_EVENT_RATE_MAP.end()) {
+ event_name = it->second.first;
+ rate_desc = it->second.second;
+ }
+ if (event_name.empty() && (GetBuildArch() == ARCH_ARM || GetBuildArch() == ARCH_ARM64)) {
+ if (auto it = ARM_EVENT_RATE_MAP.find(miss_event_name); it != ARM_EVENT_RATE_MAP.end()) {
event_name = it->second.first;
rate_desc = it->second.second;
}
- if (event_name.empty() && (GetBuildArch() == ARCH_ARM || GetBuildArch() == ARCH_ARM64)) {
- if (auto it = ARM_EVENT_RATE_MAP.find(miss_event_name); it != ARM_EVENT_RATE_MAP.end()) {
- event_name = it->second.first;
- rate_desc = it->second.second;
- }
- }
- if (event_name.empty() && android::base::ConsumeSuffix(&miss_event_name, "-misses")) {
- event_name = std::string(miss_event_name) + "s";
- rate_desc = "miss rate";
- }
- if (!event_name.empty()) {
- const CounterSummary* other = FindSummary(event_name, s.modifier, s.thread, s.cpu);
- if (other != nullptr && other->IsMonitoredAtTheSameTime(s) && other->count != 0) {
- double miss_rate = static_cast<double>(s.count) / other->count;
- return android::base::StringPrintf("%f%%%c%s", miss_rate * 100, sep, rate_desc.c_str());
- }
- }
- return "";
}
-
- bool FindRunningTimeForSummary(const CounterSummary& summary, double* running_time_in_sec) {
- for (auto& s : summaries_) {
- if ((s.type_name == "task-clock" || s.type_name == "cpu-clock") &&
- s.IsMonitoredAtTheSameTime(summary) && s.count != 0u) {
- *running_time_in_sec = s.count / 1e9;
- return true;
- }
+ if (event_name.empty() && android::base::ConsumeSuffix(&miss_event_name, "-misses")) {
+ event_name = std::string(miss_event_name) + "s";
+ rate_desc = "miss rate";
+ }
+ if (!event_name.empty()) {
+ const CounterSummary* other = FindSummary(event_name, s.modifier, s.thread, s.cpu);
+ if (other != nullptr && other->IsMonitoredAtTheSameTime(s) && other->count != 0) {
+ double miss_rate = static_cast<double>(s.count) / other->count;
+ return android::base::StringPrintf("%f%%%c%s", miss_rate * 100, sep, rate_desc.c_str());
}
- return false;
}
+ return "";
+}
- private:
- std::vector<CounterSummary> summaries_;
- bool csv_;
-};
+} // namespace simpleperf
+
+namespace {
// devfreq may use performance counters to calculate memory latency (as in
// drivers/devfreq/arm-memlat-mon.c). Hopefully we can get more available counters by asking devfreq
@@ -903,16 +790,11 @@ void StatCommand::AdjustToIntervalOnlyValues(std::vector<CountersInfo>& counters
}
for (size_t j = 0; j < counters_per_event.size(); j++) {
PerfCounter& counter = counters_per_event[j].counter;
- CounterSum& sum = last_sum[j];
- uint64_t tmp = counter.value;
- counter.value -= sum.value;
- sum.value = tmp;
- tmp = counter.time_enabled;
- counter.time_enabled -= sum.time_enabled;
- sum.time_enabled = tmp;
- tmp = counter.time_running;
- counter.time_running -= sum.time_running;
- sum.time_running = tmp;
+ CounterSum new_sum;
+ new_sum.FromCounter(counter);
+ CounterSum delta = new_sum - last_sum[j];
+ delta.ToCounter(counter);
+ last_sum[j] = new_sum;
}
}
}
@@ -948,81 +830,11 @@ bool StatCommand::ShowCounters(const std::vector<CountersInfo>& counters,
}
}
- bool counters_always_available = true;
- CounterSummaries summaries(csv_);
-
- auto add_summary = [&](const CountersInfo& info, pid_t tid, int cpu, const CounterSum& sum) {
- double scale = 1.0;
- if (sum.time_running < sum.time_enabled && sum.time_running != 0) {
- scale = static_cast<double>(sum.time_enabled) / sum.time_running;
- }
- if (system_wide_collection_ && report_per_thread_ && sum.time_running == 0) {
- // No need to report threads not running in system wide per thread report.
- return;
- }
- ThreadInfo* thread = nullptr;
- if (report_per_thread_) {
- auto it = thread_info_.find(tid);
- CHECK(it != thread_info_.end());
- thread = &it->second;
- }
- summaries.Summaries().emplace_back(info.event_name, info.event_modifier, info.group_id,
- thread, cpu, sum.value, scale, false, csv_);
- counters_always_available &= summaries.Summaries().back().IsMonitoredAllTheTime();
- };
-
- auto sort_summaries = [&](std::vector<CounterSummary>::iterator begin,
- std::vector<CounterSummary>::iterator end) {
- if (report_per_thread_ && report_per_core_) {
- // First sort by event count for all cpus in a thread, then sort by event count of each cpu.
- std::unordered_map<pid_t, uint64_t> count_per_thread;
- for (auto it = begin; it != end; ++it) {
- count_per_thread[it->thread->tid] += it->count;
- }
- std::sort(begin, end, [&](const CounterSummary& s1, const CounterSummary& s2) {
- pid_t tid1 = s1.thread->tid;
- pid_t tid2 = s2.thread->tid;
- if (tid1 != tid2) {
- if (count_per_thread[tid1] != count_per_thread[tid2]) {
- return count_per_thread[tid1] > count_per_thread[tid2];
- }
- return tid1 < tid2;
- }
- return s1.count > s2.count;
- });
- } else {
- std::sort(begin, end, [](const CounterSummary& s1, const CounterSummary& s2) {
- return s1.count > s2.count;
- });
- }
- };
-
+ CounterSummaryBuilder builder(report_per_thread_, report_per_core_, csv_, thread_info_);
for (const auto& info : counters) {
- std::unordered_map<uint64_t, CounterSum> sum_map;
- for (auto& counter : info.counters) {
- uint64_t key = 0;
- if (report_per_thread_) {
- key |= counter.tid;
- }
- if (report_per_core_) {
- key |= static_cast<uint64_t>(counter.cpu) << 32;
- }
- CounterSum& sum = sum_map[key];
- sum.value += counter.counter.value;
- sum.time_enabled = counter.counter.time_enabled;
- sum.time_running = counter.counter.time_running;
- }
- size_t pre_sum_count = summaries.Summaries().size();
- for (const auto& pair : sum_map) {
- pid_t tid = report_per_thread_ ? static_cast<pid_t>(pair.first & UINT32_MAX) : 0;
- int cpu = report_per_core_ ? static_cast<int>(pair.first >> 32) : -1;
- const CounterSum& sum = pair.second;
- add_summary(info, tid, cpu, sum);
- }
- if (report_per_thread_ || report_per_core_) {
- sort_summaries(summaries.Summaries().begin() + pre_sum_count, summaries.Summaries().end());
- }
+ builder.AddCountersForOneEventType(info);
}
+ CounterSummaries summaries(builder.Build(), csv_);
summaries.AutoGenerateSummaries();
summaries.GenerateComments(duration_in_sec);
summaries.Show(fp);
@@ -1032,26 +844,35 @@ bool StatCommand::ShowCounters(const std::vector<CountersInfo>& counters,
else
fprintf(fp, "\nTotal test time: %lf seconds.\n", duration_in_sec);
+ const char* COUNTER_MULTIPLEX_INFO =
+ "probably caused by hardware counter multiplexing (less counters than events).\n"
+ "Try --use-devfreq-counters if on a rooted device.";
+
if (cpus_ == std::vector<int>(1, -1) ||
event_selection_set_.GetMonitoredThreads() == std::set<pid_t>({-1})) {
// We either monitor a thread on all cpus, or monitor all threads on a cpu. In both cases,
// if percentages < 100%, probably it is caused by hardware counter multiplexing.
- if (!counters_always_available) {
- LOG(WARNING) << "Percentages < 100% means some events only run a subset of enabled time.\n"
- << "Probably because there are less hardware counters available than events.\n"
- << "Try --use-devfreq-counters if on a rooted device.";
+ bool counters_always_available = true;
+ for (const auto& summary : summaries.Summaries()) {
+ if (!summary.IsMonitoredAllTheTime()) {
+ counters_always_available = false;
+ break;
+ }
}
+ if (!counters_always_available) {
+ LOG(WARNING) << "Percentages < 100% means some events only run a subset of enabled time,\n"
+ << COUNTER_MULTIPLEX_INFO;
+ }
+ } else if (report_per_thread_) {
+ // We monitor each thread on each cpu.
+ LOG(INFO) << "A percentage represents runtime_on_a_cpu / runtime_on_all_cpus for each thread.\n"
+ << "If percentage sum of a thread < 99%, or report for a running thread is missing,\n"
+ << COUNTER_MULTIPLEX_INFO;
} else {
- // We monitor a thread on a cpu. A percentage represents
- // runtime_of_a_thread_on_a_cpu / runtime_of_a_thread_on_all_cpus. If percentage sum of a
- // thread < 100%, or total event count for a running thread is 0, probably it is caused by
- // hardware counter multiplexing. It is hard to detect the second case, so always print below
- // info.
- LOG(INFO) << "A percentage represents runtime_of_a_thread_on_a_cpu / "
- "runtime_of_a_thread_on_all_cpus.\n"
- << "If percentage sum of a thread < 100%, or total event count for a running\n"
- << "thread is 0, probably because there are less hardware counters available than\n"
- << "events. Try --use-devfreq-counters if on a rooted device.";
+ // We monitor some threads on each cpu.
+ LOG(INFO) << "A percentage represents runtime_on_a_cpu / runtime_on_all_cpus for monitored\n"
+ << "threads. If percentage sum < 99%, or report for an event is missing,\n"
+ << COUNTER_MULTIPLEX_INFO;
}
return true;
}
diff --git a/simpleperf/cmd_stat_impl.h b/simpleperf/cmd_stat_impl.h
new file mode 100644
index 00000000..a7f60df7
--- /dev/null
+++ b/simpleperf/cmd_stat_impl.h
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2020 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 <math.h>
+#include <sys/types.h>
+
+#include <algorithm>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+
+#include "event_selection_set.h"
+
+namespace simpleperf {
+
+struct CounterSum {
+ uint64_t value = 0;
+ uint64_t time_enabled = 0;
+ uint64_t time_running = 0;
+
+ void FromCounter(const PerfCounter& counter) {
+ value = counter.value;
+ time_enabled = counter.time_enabled;
+ time_running = counter.time_running;
+ }
+
+ void ToCounter(PerfCounter& counter) const {
+ counter.value = value;
+ counter.time_enabled = time_enabled;
+ counter.time_running = time_running;
+ }
+
+ CounterSum operator+(const CounterSum& other) const {
+ CounterSum res;
+ res.value = value + other.value;
+ res.time_enabled = time_enabled + other.time_enabled;
+ res.time_running = time_running + other.time_running;
+ return res;
+ }
+
+ CounterSum operator-(const CounterSum& other) const {
+ CounterSum res;
+ res.value = value - other.value;
+ res.time_enabled = time_enabled - other.time_enabled;
+ res.time_running = time_running - other.time_running;
+ return res;
+ }
+};
+
+struct ThreadInfo {
+ pid_t tid;
+ pid_t pid;
+ std::string name;
+};
+
+struct CounterSummary {
+ std::string type_name;
+ std::string modifier;
+ uint32_t group_id;
+ const ThreadInfo* thread;
+ int cpu; // -1 represents all cpus
+ uint64_t count;
+ uint64_t runtime_in_ns;
+ double scale;
+ std::string readable_count;
+ std::string comment;
+ bool auto_generated;
+
+ CounterSummary(const std::string& type_name, const std::string& modifier, uint32_t group_id,
+ const ThreadInfo* thread, int cpu, uint64_t count, uint64_t runtime_in_ns,
+ double scale, bool auto_generated, bool csv)
+ : type_name(type_name),
+ modifier(modifier),
+ group_id(group_id),
+ thread(thread),
+ cpu(cpu),
+ count(count),
+ runtime_in_ns(runtime_in_ns),
+ scale(scale),
+ auto_generated(auto_generated) {
+ readable_count = ReadableCountValue(csv);
+ }
+
+ bool IsMonitoredAtTheSameTime(const CounterSummary& other) const {
+ // Two summaries are monitored at the same time if they are in the same
+ // group or are monitored all the time.
+ if (group_id == other.group_id) {
+ return true;
+ }
+ return IsMonitoredAllTheTime() && other.IsMonitoredAllTheTime();
+ }
+
+ std::string Name() const {
+ if (modifier.empty()) {
+ return type_name;
+ }
+ return type_name + ":" + modifier;
+ }
+
+ bool IsMonitoredAllTheTime() const {
+ // If an event runs all the time it is enabled (by not sharing hardware
+ // counters with other events), the scale of its summary is usually within
+ // [1, 1 + 1e-5]. By setting SCALE_ERROR_LIMIT to 1e-5, We can identify
+ // events monitored all the time in most cases while keeping the report
+ // error rate <= 1e-5.
+ constexpr double SCALE_ERROR_LIMIT = 1e-5;
+ return (fabs(scale - 1.0) < SCALE_ERROR_LIMIT);
+ }
+
+ private:
+ std::string ReadableCountValue(bool csv) {
+ if (type_name == "cpu-clock" || type_name == "task-clock") {
+ // Convert nanoseconds to milliseconds.
+ double value = count / 1e6;
+ return android::base::StringPrintf("%lf(ms)", value);
+ } else {
+ // Convert big numbers to human friendly mode. For example,
+ // 1000000 will be converted to 1,000,000.
+ std::string s = android::base::StringPrintf("%" PRIu64, count);
+ if (csv) {
+ return s;
+ } else {
+ for (size_t i = s.size() - 1, j = 1; i > 0; --i, ++j) {
+ if (j == 3) {
+ s.insert(s.begin() + i, ',');
+ j = 0;
+ }
+ }
+ return s;
+ }
+ }
+ }
+};
+
+// Build a vector of CounterSummary.
+class CounterSummaryBuilder {
+ public:
+ CounterSummaryBuilder(bool report_per_thread, bool report_per_core, bool csv,
+ const std::unordered_map<pid_t, ThreadInfo>& thread_map)
+ : report_per_thread_(report_per_thread),
+ report_per_core_(report_per_core),
+ csv_(csv),
+ thread_map_(thread_map) {}
+
+ void AddCountersForOneEventType(const CountersInfo& info) {
+ std::unordered_map<uint64_t, CounterSum> sum_map;
+ for (const auto& counter : info.counters) {
+ uint64_t key = 0;
+ if (report_per_thread_) {
+ key |= counter.tid;
+ }
+ if (report_per_core_) {
+ key |= static_cast<uint64_t>(counter.cpu) << 32;
+ }
+ CounterSum& sum = sum_map[key];
+ CounterSum add;
+ add.FromCounter(counter.counter);
+ sum = sum + add;
+ }
+ size_t pre_sum_count = summaries_.size();
+ for (const auto& pair : sum_map) {
+ pid_t tid = report_per_thread_ ? static_cast<pid_t>(pair.first & UINT32_MAX) : 0;
+ int cpu = report_per_core_ ? static_cast<int>(pair.first >> 32) : -1;
+ const CounterSum& sum = pair.second;
+ AddSummary(info, tid, cpu, sum);
+ }
+ if (report_per_thread_ || report_per_core_) {
+ SortSummaries(summaries_.begin() + pre_sum_count, summaries_.end());
+ }
+ }
+
+ std::vector<CounterSummary> Build() {
+ std::vector<CounterSummary> res = std::move(summaries_);
+ summaries_.clear();
+ return res;
+ }
+
+ private:
+ void AddSummary(const CountersInfo& info, pid_t tid, int cpu, const CounterSum& sum) {
+ double scale = 1.0;
+ if (sum.time_running < sum.time_enabled && sum.time_running != 0) {
+ scale = static_cast<double>(sum.time_enabled) / sum.time_running;
+ }
+ if ((report_per_thread_ || report_per_core_) && sum.time_running == 0) {
+ // No need to report threads or cpus not running.
+ return;
+ }
+ const ThreadInfo* thread = nullptr;
+ if (report_per_thread_) {
+ auto it = thread_map_.find(tid);
+ CHECK(it != thread_map_.end());
+ thread = &it->second;
+ }
+ summaries_.emplace_back(info.event_name, info.event_modifier, info.group_id, thread, cpu,
+ sum.value, sum.time_running, scale, false, csv_);
+ }
+
+ void SortSummaries(std::vector<CounterSummary>::iterator begin,
+ std::vector<CounterSummary>::iterator end) {
+ if (report_per_thread_ && report_per_core_) {
+ // First sort by event count for all cpus in a thread, then sort by event count of each cpu.
+ std::unordered_map<pid_t, uint64_t> count_per_thread;
+ for (auto it = begin; it != end; ++it) {
+ count_per_thread[it->thread->tid] += it->count;
+ }
+ std::sort(begin, end, [&](const CounterSummary& s1, const CounterSummary& s2) {
+ pid_t tid1 = s1.thread->tid;
+ pid_t tid2 = s2.thread->tid;
+ if (tid1 != tid2) {
+ if (count_per_thread[tid1] != count_per_thread[tid2]) {
+ return count_per_thread[tid1] > count_per_thread[tid2];
+ }
+ return tid1 < tid2;
+ }
+ return s1.count > s2.count;
+ });
+ } else {
+ std::sort(begin, end, [](const CounterSummary& s1, const CounterSummary& s2) {
+ return s1.count > s2.count;
+ });
+ }
+ };
+
+ const bool report_per_thread_;
+ const bool report_per_core_;
+ const bool csv_;
+ const std::unordered_map<pid_t, ThreadInfo>& thread_map_;
+ std::vector<CounterSummary> summaries_;
+};
+
+class CounterSummaries {
+ public:
+ explicit CounterSummaries(std::vector<CounterSummary>&& summaries, bool csv)
+ : summaries_(std::move(summaries)), csv_(csv) {}
+ const std::vector<CounterSummary>& Summaries() { return summaries_; }
+
+ const CounterSummary* FindSummary(const std::string& type_name, const std::string& modifier,
+ const ThreadInfo* thread, int cpu);
+
+ // If we have two summaries monitoring the same event type at the same time,
+ // that one is for user space only, and the other is for kernel space only;
+ // then we can automatically generate a summary combining the two results.
+ // For example, a summary of branch-misses:u and a summary for branch-misses:k
+ // can generate a summary of branch-misses.
+ void AutoGenerateSummaries();
+ void GenerateComments(double duration_in_sec);
+ void Show(FILE* fp);
+ void ShowCSV(FILE* fp);
+ void ShowText(FILE* fp);
+
+ private:
+ std::string GetCommentForSummary(const CounterSummary& s, double duration_in_sec);
+ std::string GetRateComment(const CounterSummary& s, char sep);
+ bool FindRunningTimeForSummary(const CounterSummary& summary, double* running_time_in_sec);
+
+ private:
+ std::vector<CounterSummary> summaries_;
+ bool csv_;
+};
+
+} // namespace simpleperf \ No newline at end of file
diff --git a/simpleperf/cmd_stat_test.cpp b/simpleperf/cmd_stat_test.cpp
index bbeb4b66..6b6196a9 100644
--- a/simpleperf/cmd_stat_test.cpp
+++ b/simpleperf/cmd_stat_test.cpp
@@ -22,12 +22,15 @@
#include <thread>
+#include "cmd_stat_impl.h"
#include "command.h"
#include "environment.h"
#include "event_selection_set.h"
#include "get_test_data.h"
#include "test_util.h"
+using namespace simpleperf;
+
static std::unique_ptr<Command> StatCmd() {
return CreateCommandInstance("stat");
}
@@ -341,3 +344,207 @@ TEST(stat_cmd, per_core_option) {
ASSERT_TRUE(StatCmd()->Run({"--per-core", "sleep", "0.1"}));
TEST_IN_ROOT(StatCmd()->Run({"--per-core", "-a", "--duration", "0.1"}));
}
+
+TEST(stat_cmd, counter_sum) {
+ PerfCounter counter;
+ counter.value = 1;
+ counter.time_enabled = 2;
+ counter.time_running = 3;
+ CounterSum a;
+ a.FromCounter(counter);
+ ASSERT_EQ(a.value, 1);
+ ASSERT_EQ(a.time_enabled, 2);
+ ASSERT_EQ(a.time_running, 3);
+ CounterSum b = a + a;
+ ASSERT_EQ(b.value, 2);
+ ASSERT_EQ(b.time_enabled, 4);
+ ASSERT_EQ(b.time_running, 6);
+ CounterSum c = a - a;
+ ASSERT_EQ(c.value, 0);
+ ASSERT_EQ(c.time_enabled, 0);
+ ASSERT_EQ(c.time_running, 0);
+ b.ToCounter(counter);
+ ASSERT_EQ(counter.value, 2);
+ ASSERT_EQ(counter.time_enabled, 4);
+ ASSERT_EQ(counter.time_running, 6);
+}
+
+class StatCmdSummaryBuilderTest : public ::testing::Test {
+ protected:
+ void AddCounter(int event_id, pid_t tid, int cpu, int value, int time_enabled, int time_running) {
+ if (thread_map_.count(tid) == 0) {
+ ThreadInfo& thread = thread_map_[tid];
+ thread.pid = thread.tid = tid;
+ thread.name = "thread" + std::to_string(tid);
+ }
+ if (event_id >= counters_.size()) {
+ counters_.resize(event_id + 1);
+ counters_[event_id].group_id = 0;
+ counters_[event_id].event_name = "event" + std::to_string(event_id);
+ }
+ CountersInfo& info = counters_[event_id];
+ info.counters.resize(info.counters.size() + 1);
+ CounterInfo& counter = info.counters.back();
+ counter.tid = tid;
+ counter.cpu = cpu;
+ counter.counter.id = 0;
+ counter.counter.value = value;
+ counter.counter.time_enabled = time_enabled;
+ counter.counter.time_running = time_running;
+ }
+
+ std::vector<CounterSummary> BuildSummary(bool report_per_thread, bool report_per_core) {
+ CounterSummaryBuilder builder(report_per_thread, report_per_core, false, thread_map_);
+ for (auto& info : counters_) {
+ builder.AddCountersForOneEventType(info);
+ }
+ return builder.Build();
+ }
+
+ std::unordered_map<pid_t, ThreadInfo> thread_map_;
+ std::vector<CountersInfo> counters_;
+};
+
+TEST_F(StatCmdSummaryBuilderTest, multiple_events) {
+ AddCounter(0, 0, 0, 1, 1, 1);
+ AddCounter(1, 0, 0, 2, 2, 2);
+ std::vector<CounterSummary> summaries = BuildSummary(false, false);
+ ASSERT_EQ(summaries.size(), 2);
+ ASSERT_EQ(summaries[0].type_name, "event0");
+ ASSERT_EQ(summaries[0].count, 1);
+ ASSERT_NEAR(summaries[0].scale, 1.0, 1e-5);
+ ASSERT_EQ(summaries[1].type_name, "event1");
+ ASSERT_EQ(summaries[1].count, 2);
+ ASSERT_NEAR(summaries[1].scale, 1.0, 1e-5);
+}
+
+TEST_F(StatCmdSummaryBuilderTest, default_aggregate) {
+ AddCounter(0, 0, 0, 1, 1, 1);
+ AddCounter(0, 0, 1, 1, 1, 1);
+ AddCounter(0, 1, 0, 1, 1, 1);
+ AddCounter(0, 1, 1, 2, 2, 1);
+ std::vector<CounterSummary> summaries = BuildSummary(false, false);
+ ASSERT_EQ(summaries.size(), 1);
+ ASSERT_EQ(summaries[0].count, 5);
+ ASSERT_NEAR(summaries[0].scale, 1.25, 1e-5);
+}
+
+TEST_F(StatCmdSummaryBuilderTest, per_thread_aggregate) {
+ AddCounter(0, 0, 0, 1, 1, 1);
+ AddCounter(0, 0, 1, 1, 1, 1);
+ AddCounter(0, 1, 0, 1, 1, 1);
+ AddCounter(0, 1, 1, 2, 2, 1);
+ std::vector<CounterSummary> summaries = BuildSummary(true, false);
+ ASSERT_EQ(summaries.size(), 2);
+ ASSERT_EQ(summaries[0].thread->tid, 1);
+ ASSERT_EQ(summaries[0].cpu, -1);
+ ASSERT_EQ(summaries[0].count, 3);
+ ASSERT_NEAR(summaries[0].scale, 1.5, 1e-5);
+ ASSERT_EQ(summaries[1].thread->tid, 0);
+ ASSERT_EQ(summaries[0].cpu, -1);
+ ASSERT_EQ(summaries[1].count, 2);
+ ASSERT_NEAR(summaries[1].scale, 1.0, 1e-5);
+}
+
+TEST_F(StatCmdSummaryBuilderTest, per_core_aggregate) {
+ AddCounter(0, 0, 0, 1, 1, 1);
+ AddCounter(0, 0, 1, 1, 1, 1);
+ AddCounter(0, 1, 0, 1, 1, 1);
+ AddCounter(0, 1, 1, 2, 2, 1);
+ std::vector<CounterSummary> summaries = BuildSummary(false, true);
+ ASSERT_EQ(summaries.size(), 2);
+ ASSERT_TRUE(summaries[0].thread == nullptr);
+ ASSERT_EQ(summaries[0].cpu, 1);
+ ASSERT_EQ(summaries[0].count, 3);
+ ASSERT_NEAR(summaries[0].scale, 1.5, 1e-5);
+ ASSERT_TRUE(summaries[1].thread == nullptr);
+ ASSERT_EQ(summaries[1].cpu, 0);
+ ASSERT_EQ(summaries[1].count, 2);
+ ASSERT_NEAR(summaries[1].scale, 1.0, 1e-5);
+}
+
+TEST_F(StatCmdSummaryBuilderTest, per_thread_core_aggregate) {
+ AddCounter(0, 0, 0, 1, 1, 1);
+ AddCounter(0, 0, 1, 2, 1, 1);
+ AddCounter(0, 1, 0, 3, 1, 1);
+ AddCounter(0, 1, 1, 4, 2, 1);
+ std::vector<CounterSummary> summaries = BuildSummary(true, true);
+ ASSERT_EQ(summaries.size(), 4);
+ ASSERT_EQ(summaries[0].thread->tid, 1);
+ ASSERT_EQ(summaries[0].cpu, 1);
+ ASSERT_EQ(summaries[0].count, 4);
+ ASSERT_NEAR(summaries[0].scale, 2.0, 1e-5);
+ ASSERT_EQ(summaries[1].thread->tid, 1);
+ ASSERT_EQ(summaries[1].cpu, 0);
+ ASSERT_EQ(summaries[1].count, 3);
+ ASSERT_NEAR(summaries[1].scale, 1.0, 1e-5);
+ ASSERT_EQ(summaries[2].thread->tid, 0);
+ ASSERT_EQ(summaries[2].cpu, 1);
+ ASSERT_EQ(summaries[2].count, 2);
+ ASSERT_NEAR(summaries[2].scale, 1.0, 1e-5);
+ ASSERT_EQ(summaries[3].thread->tid, 0);
+ ASSERT_EQ(summaries[3].cpu, 0);
+ ASSERT_EQ(summaries[3].count, 1);
+ ASSERT_NEAR(summaries[3].scale, 1.0, 1e-5);
+}
+
+class StatCmdSummariesTest : public ::testing::Test {
+ protected:
+ void AddSummary(const std::string event_name, pid_t tid, int cpu, uint64_t count,
+ uint64_t runtime_in_ns) {
+ ThreadInfo* thread = nullptr;
+ if (tid != -1) {
+ thread = &thread_map_[tid];
+ }
+ summary_v_.emplace_back(event_name, "", 0, thread, cpu, count, runtime_in_ns, 1.0, false,
+ false);
+ }
+
+ const std::string* GetComment(size_t index) {
+ if (!summaries_) {
+ summaries_.reset(new CounterSummaries(std::move(summary_v_), false));
+ summaries_->GenerateComments(1.0);
+ }
+ if (index < summaries_->Summaries().size()) {
+ return &(summaries_->Summaries()[index].comment);
+ }
+ return nullptr;
+ }
+
+ std::unordered_map<pid_t, ThreadInfo> thread_map_;
+ std::vector<CounterSummary> summary_v_;
+ std::unique_ptr<CounterSummaries> summaries_;
+};
+
+TEST_F(StatCmdSummariesTest, task_clock_comment) {
+ AddSummary("task-clock", -1, -1, 1e9, 0);
+ AddSummary("task-clock", 0, -1, 2e9, 0);
+ AddSummary("task-clock", -1, 0, 0.5e9, 0);
+ AddSummary("task-clock", 1, 1, 3e9, 0);
+ ASSERT_EQ(*GetComment(0), "1.000000 cpus used");
+ ASSERT_EQ(*GetComment(1), "2.000000 cpus used");
+ ASSERT_EQ(*GetComment(2), "0.500000 cpus used");
+ ASSERT_EQ(*GetComment(3), "3.000000 cpus used");
+}
+
+TEST_F(StatCmdSummariesTest, cpu_cycles_comment) {
+ AddSummary("cpu-cycles", -1, -1, 100, 100);
+ AddSummary("cpu-cycles", 0, -1, 200, 100);
+ AddSummary("cpu-cycles", -1, 0, 50, 100);
+ AddSummary("cpu-cycles", 1, 1, 300, 100);
+ ASSERT_EQ(*GetComment(0), "1.000000 GHz");
+ ASSERT_EQ(*GetComment(1), "2.000000 GHz");
+ ASSERT_EQ(*GetComment(2), "0.500000 GHz");
+ ASSERT_EQ(*GetComment(3), "3.000000 GHz");
+}
+
+TEST_F(StatCmdSummariesTest, rate_comment) {
+ AddSummary("branch-misses", -1, -1, 1e9, 1e9);
+ AddSummary("branch-misses", 0, -1, 1e6, 1e9);
+ AddSummary("branch-misses", -1, 0, 1e3, 1e9);
+ AddSummary("branch-misses", 1, 1, 1, 1e9);
+ ASSERT_EQ(*GetComment(0), "1.000 G/sec");
+ ASSERT_EQ(*GetComment(1), "1.000 M/sec");
+ ASSERT_EQ(*GetComment(2), "1.000 K/sec");
+ ASSERT_EQ(*GetComment(3), "1.000 /sec");
+} \ No newline at end of file
diff --git a/simpleperf/doc/executable_commands_reference.md b/simpleperf/doc/executable_commands_reference.md
index 22ed52fe..5d51a158 100644
--- a/simpleperf/doc/executable_commands_reference.md
+++ b/simpleperf/doc/executable_commands_reference.md
@@ -294,10 +294,26 @@ $ su 0 simpleperf stat --per-thread -a --interval 1000 --interval-only-values --
By default, stat cmd outputs an event count sum for all monitored cpu cores. But when `--per-core`
option is used, stat cmd outputs an event count for each core. It can be used to see how events
are distributed on different cores.
+When stating non-system wide with `--per-core` option, simpleperf creates a perf event for each
+monitored thread on each core. When a thread is in running state, perf events on all cores are
+enabled, but only the perf event on the core running the thread is in running state. So the
+percentage comment shows runtime_on_a_core / runtime_on_all_cores. Note that, percentage is still
+affected by hardware counter multiplexing. Check simpleperf log output for ways to distinguish it.
```sh
# Print event counts for each cpu running threads in process 11904.
+# A percentage shows runtime_on_a_cpu / runtime_on_all_cpus.
$ simpleperf stat --per-core -p 11904 --duration 1
+Performance counter statistics:
+
+# cpu count event_name # percentage = event_run_time / enabled_time
+ 7 56,552,838 cpu-cycles # (60%)
+ 3 25,958,605 cpu-cycles # (20%)
+ 0 22,822,698 cpu-cycles # (15%)
+ 1 6,661,495 cpu-cycles # (5%)
+ 4 1,519,093 cpu-cycles # (0%)
+
+Total test time: 1.001082 seconds.
# Print event counts for each cpu system wide.
$ su 0 simpleperf stat --per-core -a --duration 1
diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp
index af14fa3f..5b2fcaa8 100644
--- a/simpleperf/environment.cpp
+++ b/simpleperf/environment.cpp
@@ -296,12 +296,18 @@ static bool ReadPerfEventParanoid(int* value) {
}
bool CanRecordRawData() {
- if (GetAndroidVersion() >= 11) {
- // On Android R, tracepoint raw data is disabled by selinux.
- return IsRoot();
+ if (IsRoot()) {
+ return true;
}
+#if defined(__ANDROID__)
+ // Android R uses selinux to control perf_event_open. Whether raw data can be recorded is hard
+ // to check unless we really try it. And probably there is no need to record raw data in non-root
+ // users.
+ return false;
+#else
int value;
return ReadPerfEventParanoid(&value) && value == -1;
+#endif
}
static const char* GetLimitLevelDescription(int limit_level) {
diff --git a/tests/kernel.config/Android.mk b/tests/kernel.config/Android.mk
index 0c3fedd0..a622bf56 100644
--- a/tests/kernel.config/Android.mk
+++ b/tests/kernel.config/Android.mk
@@ -51,7 +51,7 @@ LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
LOCAL_SHARED_LIBRARIES := libbase
LOCAL_STATIC_LIBRARIES := libgtest libgtest_main
-LOCAL_COMPATIBILITY_SUITE := cts vts
+LOCAL_COMPATIBILITY_SUITE := cts vts10
LOCAL_CTS_TEST_PACKAGE := android.kernel.config
include $(BUILD_CTS_EXECUTABLE)
diff --git a/verity/Android.bp b/verity/Android.bp
index 1158c77d..5d0a80c0 100644
--- a/verity/Android.bp
+++ b/verity/Android.bp
@@ -51,6 +51,7 @@ cc_binary_host {
static_libs: [
"libfec",
"libfec_rs",
+ "libavb",
"libcrypto_utils",
"libcrypto",
"libext4_utils",
diff --git a/verity/fec/Android.bp b/verity/fec/Android.bp
index a6bcef95..4bcecb09 100644
--- a/verity/fec/Android.bp
+++ b/verity/fec/Android.bp
@@ -7,6 +7,11 @@ cc_binary_host {
misc_undefined: ["integer"],
},
},
+ linux: {
+ static_libs: [
+ "libavb",
+ ],
+ },
},
srcs: [