summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWei Wang <wvw@google.com>2018-10-19 21:57:04 -0700
committerWei Wang <wvw@google.com>2018-10-20 19:30:29 -0700
commita4823198aa21009c3a617bfed785d27de567afae (patch)
tree97563d7cc09bd895dec903161381390cdfe276aa
parentc65981918a178cc2df961780b8a55560a096ed99 (diff)
downloadextras-a4823198aa21009c3a617bfed785d27de567afae.tar.gz
libperfmgr: Add a new type of node to manage property
PowerHAL sometimes need set properties, adding a new type of node to support this usage. Change-Id: I0c04df831d10f16a37f6ae5ae234a9ef34fc5f54 Merged-In: I18a4a50993e262c59460dc5e6cbace22b2b50047 Fixes: 116731391 Bug: 110166984 Test: Add new unit tests and libperfmgr_test passing
-rw-r--r--libperfmgr/Android.bp5
-rw-r--r--libperfmgr/FileNode.cc104
-rw-r--r--libperfmgr/HintManager.cc47
-rw-r--r--libperfmgr/Node.cc82
-rw-r--r--libperfmgr/PropertyNode.cc78
-rw-r--r--libperfmgr/RequestGroup.cc2
-rw-r--r--libperfmgr/config_schema.json10
-rw-r--r--libperfmgr/include/perfmgr/FileNode.h55
-rw-r--r--libperfmgr/include/perfmgr/Node.h22
-rw-r--r--libperfmgr/include/perfmgr/PropertyNode.h48
-rw-r--r--libperfmgr/include/perfmgr/RequestGroup.h2
-rw-r--r--libperfmgr/tests/FileNodeTest.cc (renamed from libperfmgr/tests/NodeTest.cc)48
-rw-r--r--libperfmgr/tests/HintManagerTest.cc144
-rw-r--r--libperfmgr/tests/NodeLooperThreadTest.cc13
-rw-r--r--libperfmgr/tests/PropertyNodeTest.cc220
15 files changed, 719 insertions, 161 deletions
diff --git a/libperfmgr/Android.bp b/libperfmgr/Android.bp
index effc057e..5fae64fc 100644
--- a/libperfmgr/Android.bp
+++ b/libperfmgr/Android.bp
@@ -39,6 +39,8 @@ cc_library {
srcs: [
"RequestGroup.cc",
"Node.cc",
+ "FileNode.cc",
+ "PropertyNode.cc",
"NodeLooperThread.cc",
"HintManager.cc",
]
@@ -50,7 +52,8 @@ cc_test {
static_libs: ["libperfmgr"],
srcs: [
"tests/RequestGroupTest.cc",
- "tests/NodeTest.cc",
+ "tests/FileNodeTest.cc",
+ "tests/PropertyNodeTest.cc",
"tests/NodeLooperThreadTest.cc",
"tests/HintManagerTest.cc",
]
diff --git a/libperfmgr/FileNode.cc b/libperfmgr/FileNode.cc
new file mode 100644
index 00000000..1d051977
--- /dev/null
+++ b/libperfmgr/FileNode.cc
@@ -0,0 +1,104 @@
+/*
+ * 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/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(name, node_path, std::move(req_sorted), default_val_index,
+ reset_on_init),
+ hold_fd_(hold_fd) {
+ if (reset_on_init) {
+ Update();
+ }
+}
+
+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();
+
+ 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();
+ }
+ // 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
index f4e64f8e..cdce043d 100644
--- a/libperfmgr/HintManager.cc
+++ b/libperfmgr/HintManager.cc
@@ -16,6 +16,7 @@
#define LOG_TAG "libperfmgr"
+#include <algorithm>
#include <set>
#include <android-base/file.h>
@@ -24,7 +25,9 @@
#include <json/reader.h>
#include <json/value.h>
+#include "perfmgr/FileNode.h"
#include "perfmgr/HintManager.h"
+#include "perfmgr/PropertyNode.h"
namespace android {
namespace perfmgr {
@@ -222,20 +225,44 @@ std::vector<std::unique_ptr<Node>> HintManager::ParseNodes(
} else {
reset = nodes[i]["ResetOnInit"].asBool();
}
- LOG(VERBOSE) << "Node[" << i << "]'s ResetOnInit: " << reset;
+ LOG(VERBOSE) << "Node[" << i << "]'s ResetOnInit: " << reset ? "true"
+ : "false";
- 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'";
+ 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 {
- hold_fd = nodes[i]["HoldFd"].asBool();
+ LOG(ERROR) << "Invalid Node[" << i
+ << "]'s Type: only File and Property supported.";
+ nodes_parsed.clear();
+ return nodes_parsed;
}
- LOG(VERBOSE) << "Node[" << i << "]'s HoldFd: " << hold_fd;
- nodes_parsed.emplace_back(std::make_unique<Node>(
- name, path, values_parsed, static_cast<std::size_t>(default_index),
- reset, hold_fd));
+ 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: " << hold_fd;
+
+ 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;
diff --git a/libperfmgr/Node.cc b/libperfmgr/Node.cc
index c35c3ca1..90251014 100644
--- a/libperfmgr/Node.cc
+++ b/libperfmgr/Node.cc
@@ -28,22 +28,16 @@ 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, bool hold_fd)
+ bool reset_on_init)
: name_(name),
node_path_(node_path),
req_sorted_(std::move(req_sorted)),
default_val_index_(default_val_index),
reset_on_init_(reset_on_init),
- hold_fd_(hold_fd) {
- if (reset_on_init) {
- // Assigning an invalid value so the next Update() will update the
- // Node's value to default
- current_val_index_ = req_sorted_.size();
- Update();
- } else {
- current_val_index_ = default_val_index;
- }
-}
+ // 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) {
@@ -66,55 +60,11 @@ bool Node::RemoveRequest(const std::string& hint_type) {
return ret;
}
-std::chrono::milliseconds Node::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_) {
- std::string req_value = req_sorted_[value_index].GetRequestValue();
-
- 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();
- }
- // Update current index only when succeed
- current_val_index_ = value_index;
- }
- }
- return expire_time;
-}
-
-std::string Node::GetName() const {
+const std::string& Node::GetName() const {
return name_;
}
-std::string Node::GetPath() const {
+const std::string& Node::GetPath() const {
return node_path_;
}
@@ -138,10 +88,6 @@ bool Node::GetResetOnInit() const {
return reset_on_init_;
}
-bool Node::GetHoldFd() const {
- return hold_fd_;
-}
-
std::vector<std::string> Node::GetValues() const {
std::vector<std::string> values;
for (const auto& value : req_sorted_) {
@@ -150,19 +96,5 @@ std::vector<std::string> Node::GetValues() const {
return values;
}
-void Node::DumpToFd(int fd) {
- 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/PropertyNode.cc b/libperfmgr/PropertyNode.cc
new file mode 100644
index 00000000..cf07b3cc
--- /dev/null
+++ b/libperfmgr/PropertyNode.cc
@@ -0,0 +1,78 @@
+/*
+ * 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(name, node_path, std::move(req_sorted), default_val_index,
+ reset_on_init) {
+ if (reset_on_init) {
+ Update();
+ }
+}
+
+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
index 7f7ddf4b..857d17a4 100644
--- a/libperfmgr/RequestGroup.cc
+++ b/libperfmgr/RequestGroup.cc
@@ -37,7 +37,7 @@ bool RequestGroup::RemoveRequest(const std::string& hint_type) {
return request_map_.erase(hint_type);
}
-std::string RequestGroup::GetRequestValue() const {
+const std::string& RequestGroup::GetRequestValue() const {
return request_value_;
}
diff --git a/libperfmgr/config_schema.json b/libperfmgr/config_schema.json
index b74bacbe..c26f9555 100644
--- a/libperfmgr/config_schema.json
+++ b/libperfmgr/config_schema.json
@@ -33,7 +33,7 @@
"type": "string",
"id": "/properties/Nodes/items/properties/Path",
"title": "The Path Schema.",
- "description": "The path of the node.",
+ "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": {
@@ -62,11 +62,17 @@
"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."
+ "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."
}
}
}
diff --git a/libperfmgr/include/perfmgr/FileNode.h b/libperfmgr/include/perfmgr/FileNode.h
new file mode 100644
index 00000000..6d6c2190
--- /dev/null
+++ b/libperfmgr/include/perfmgr/FileNode.h
@@ -0,0 +1,55 @@
+/*
+ * 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 = true) 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/Node.h b/libperfmgr/include/perfmgr/Node.h
index 560979b0..7dc35413 100644
--- a/libperfmgr/include/perfmgr/Node.h
+++ b/libperfmgr/include/perfmgr/Node.h
@@ -46,9 +46,7 @@ namespace perfmgr {
// protection from caller e.g. NodeLooperThread.
class Node {
public:
- Node(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);
+ virtual ~Node() {}
// Return true if successfully add a request
bool AddRequest(std::size_t value_index, const std::string& hint_type,
@@ -61,18 +59,20 @@ class Node {
// 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.
- std::chrono::milliseconds Update(bool log_error = true);
+ virtual std::chrono::milliseconds Update(bool log_error = true) = 0;
- std::string GetName() const;
- std::string GetPath() const;
+ 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 GetHoldFd() const;
bool GetValueIndex(const std::string value, std::size_t* index) const;
- void DumpToFd(int fd);
+ virtual void DumpToFd(int fd) const = 0;
- private:
+ 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;
@@ -81,10 +81,8 @@ class Node {
// request vector, one entry per possible value, sorted by priority
std::vector<RequestGroup> req_sorted_;
const std::size_t default_val_index_;
- std::size_t current_val_index_;
const bool reset_on_init_;
- const bool hold_fd_;
- android::base::unique_fd fd_;
+ std::size_t current_val_index_;
};
} // namespace perfmgr
diff --git a/libperfmgr/include/perfmgr/PropertyNode.h b/libperfmgr/include/perfmgr/PropertyNode.h
new file mode 100644
index 00000000..8c40e4e1
--- /dev/null
+++ b/libperfmgr/include/perfmgr/PropertyNode.h
@@ -0,0 +1,48 @@
+/*
+ * 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 = true) 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
index 144adc2d..2d997b59 100644
--- a/libperfmgr/include/perfmgr/RequestGroup.h
+++ b/libperfmgr/include/perfmgr/RequestGroup.h
@@ -45,7 +45,7 @@ class RequestGroup {
// request_map_ is empty.
bool GetExpireTime(std::chrono::milliseconds* expire_time);
// Return the request value.
- std::string GetRequestValue() const;
+ 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);
diff --git a/libperfmgr/tests/NodeTest.cc b/libperfmgr/tests/FileNodeTest.cc
index 02429ccd..3bec8510 100644
--- a/libperfmgr/tests/NodeTest.cc
+++ b/libperfmgr/tests/FileNodeTest.cc
@@ -23,7 +23,7 @@
#include <gtest/gtest.h>
-#include "perfmgr/Node.h"
+#include "perfmgr/FileNode.h"
namespace android {
namespace perfmgr {
@@ -41,26 +41,27 @@ static inline void _VerifyPathValue(const std::string& path,
}
// Test init with no default value
-TEST(NodeTest, NoInitDefaultTest) {
+TEST(FileNodeTest, NoInitDefaultTest) {
TemporaryFile tf;
- Node t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, false);
+ FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, false);
_VerifyPathValue(tf.path, "");
}
// Test init with default value
-TEST(NodeTest, InitDefaultTest) {
+TEST(FileNodeTest, InitDefaultTest) {
TemporaryFile tf;
- Node t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, true);
+ FileNode t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, true);
_VerifyPathValue(tf.path, "value1");
TemporaryFile tf2;
- Node t2("t2", tf2.path, {{"value0"}, {"value1"}, {"value2"}}, 0, true);
+ FileNode t2("t2", tf2.path, {{"value0"}, {"value1"}, {"value2"}}, 0, true);
_VerifyPathValue(tf2.path, "value0");
}
// Test DumpToFd
-TEST(NodeTest, DumpToFdTest) {
+TEST(FileNodeTest, DumpToFdTest) {
TemporaryFile tf;
- Node t("test_dump", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, true);
+ FileNode t("test_dump", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1,
+ true);
TemporaryFile dumptf;
t.DumpToFd(dumptf.fd);
fsync(dumptf.fd);
@@ -70,9 +71,9 @@ TEST(NodeTest, DumpToFdTest) {
}
// Test GetValueIndex
-TEST(NodeTest, GetValueIndexTest) {
+TEST(FileNodeTest, GetValueIndexTest) {
TemporaryFile tf;
- Node t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, false);
+ 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);
@@ -82,9 +83,9 @@ TEST(NodeTest, GetValueIndexTest) {
}
// Test GetValues
-TEST(NodeTest, GetValuesTest) {
+TEST(FileNodeTest, GetValuesTest) {
TemporaryFile tf;
- Node t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 1, false);
+ 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]);
@@ -93,10 +94,10 @@ TEST(NodeTest, GetValuesTest) {
}
// Test get more properties
-TEST(NodeTest, GetPropertiesTest) {
+TEST(FileNodeTest, GetPropertiesTest) {
std::string test_name = "TESTREQ_1";
std::string test_path = "TEST_PATH";
- Node t(test_name, test_path, {}, 0, false, true);
+ 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());
@@ -106,9 +107,9 @@ TEST(NodeTest, GetPropertiesTest) {
}
// Test add request fail and retry
-TEST(NodeTest, AddRequestTestFail) {
- Node t("t", "/sys/android/nonexist_node_test",
- {{"value0"}, {"value1"}, {"value2"}}, 2, true);
+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();
@@ -124,9 +125,9 @@ TEST(NodeTest, AddRequestTestFail) {
}
// Test add request
-TEST(NodeTest, AddRequestTest) {
+TEST(FileNodeTest, AddRequestTest) {
TemporaryFile tf;
- Node t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 2, true);
+ 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();
@@ -154,9 +155,9 @@ TEST(NodeTest, AddRequestTest) {
}
// Test remove request
-TEST(NodeTest, RemoveRequestTest) {
+TEST(FileNodeTest, RemoveRequestTest) {
TemporaryFile tf;
- Node t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 2, true);
+ 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();
@@ -184,9 +185,10 @@ TEST(NodeTest, RemoveRequestTest) {
}
// Test add request with holding fd
-TEST(NodeTest, AddRequestTestHoldFdOverride) {
+TEST(FileNodeTest, AddRequestTestHoldFdOverride) {
TemporaryFile tf;
- Node t("t", tf.path, {{"value0"}, {"value1"}, {"value2"}}, 2, true, true);
+ 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));
diff --git a/libperfmgr/tests/HintManagerTest.cc b/libperfmgr/tests/HintManagerTest.cc
index 2355b8ff..33a3ad16 100644
--- a/libperfmgr/tests/HintManagerTest.cc
+++ b/libperfmgr/tests/HintManagerTest.cc
@@ -19,11 +19,14 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/test_utils.h>
#include <gtest/gtest.h>
+#include "perfmgr/FileNode.h"
#include "perfmgr/HintManager.h"
+#include "perfmgr/PropertyNode.h"
namespace android {
namespace perfmgr {
@@ -55,6 +58,16 @@ constexpr auto kSLEEP_TOLERANCE_MS = 50ms;
// "384000"
// ],
// "HoldFd": true
+// },
+// {
+// "Name": "ModeProperty",
+// "Path": "vendor.pwhal.mode",
+// "Values": [
+// "HIGH",
+// "LOW",
+// "NONE"
+// ],
+// "Type": "Property"
// }
// ],
// "Actions": [
@@ -65,6 +78,12 @@ constexpr auto kSLEEP_TOLERANCE_MS = 50ms;
// "Duration": 800
// },
// {
+// "PowerHint": "INTERACTION",
+// "Node": "ModeProperty",
+// "Value": "LOW",
+// "Duration": 800
+// },
+// {
// "PowerHint": "LAUNCH",
// "Node": "CPUCluster0MinFreq",
// "Value": "1134000",
@@ -72,6 +91,12 @@ constexpr auto kSLEEP_TOLERANCE_MS = 50ms;
// },
// {
// "PowerHint": "LAUNCH",
+// "Node": "ModeProperty",
+// "Value": "HIGH",
+// "Duration": 500
+// },
+// {
+// "PowerHint": "LAUNCH",
// "Node": "CPUCluster1MinFreq",
// "Value": "1512000",
// "Duration": 2000
@@ -85,13 +110,16 @@ constexpr char kJSON_RAW[] =
"\"DefaultIndex\":2,\"ResetOnInit\":true},{\"Name\":\"CPUCluster1MinFreq\","
"\"Path\":\"/sys/devices/system/cpu/cpu4/cpufreq/"
"scaling_min_freq\",\"Values\":[\"1512000\",\"1134000\",\"384000\"],"
- "\"HoldFd\":true}],\"Actions\":[{\"PowerHint\":\"INTERACTION\",\"Node\":"
+ "\"HoldFd\":true},{\"Name\":\"ModeProperty\",\"Path\":\"vendor.pwhal."
+ "mode\",\"Values\":[\"HIGH\",\"LOW\",\"NONE\"],\"Type\":\"Property\"}],"
+ "\"Actions\":[{\"PowerHint\":\"INTERACTION\",\"Node\":"
"\"CPUCluster1MinFreq\",\"Value\":\"1134000\",\"Duration\":800},{"
- "\"PowerHint\":"
- "\"LAUNCH\",\"Node\":\"CPUCluster0MinFreq\",\"Value\":\"1134000\","
- "\"Duration\":"
- "500},{\"PowerHint\":\"LAUNCH\",\"Node\":\"CPUCluster1MinFreq\","
- "\"Value\":\"1512000\",\"Duration\":2000}]}";
+ "\"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:
@@ -99,36 +127,42 @@ class HintManagerTest : public ::testing::Test, public HintManager {
: HintManager(nullptr,
std::map<std::string, std::vector<NodeAction>>{}) {
android::base::SetMinimumLogSeverity(android::base::VERBOSE);
+ prop_ = "vendor.pwhal.mode";
}
virtual void SetUp() {
- // Set up dummy nodes
+ // Set up 3 dummy nodes
std::unique_ptr<TemporaryFile> tf = std::make_unique<TemporaryFile>();
- nodes_.emplace_back(
- new Node("n0", tf->path,
- {{"n0_value0"}, {"n0_value1"}, {"n0_value2"}}, 2, false));
+ 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 Node("n1", tf->path,
- {{"n1_value0"}, {"n1_value1"}, {"n1_value2"}}, 2, true));
+ 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}}},
- {"LAUNCH", {{0, 0, 0ms}, {1, 0, 400ms}}}};
+ {"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 filepath
+ // replace file path
json_doc_ = kJSON_RAW;
std::string from =
"/sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq";
@@ -137,6 +171,8 @@ class HintManagerTest : public ::testing::Test, public HintManager {
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() {
@@ -150,8 +186,15 @@ class HintManagerTest : public ::testing::Test, public HintManager {
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;
@@ -176,9 +219,10 @@ TEST_F(HintManagerTest, HintInitDefaultTest) {
EXPECT_TRUE(hm.IsRunning());
_VerifyPathValue(files_[0]->path, "");
_VerifyPathValue(files_[1]->path, "n1_value2");
+ _VerifyPropertyValue(prop_, "");
}
-// Test hint/cancel/expire
+// Test hint/cancel/expire with dummy actions
TEST_F(HintManagerTest, HintTest) {
HintManager hm(nm_, actions_);
EXPECT_TRUE(hm.IsRunning());
@@ -186,6 +230,7 @@ TEST_F(HintManagerTest, HintTest) {
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
@@ -193,36 +238,42 @@ TEST_F(HintManagerTest, HintTest) {
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));
- // no"LAUNCH" node1 not expired
+ // "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 with duplicate name
+// Test parsing nodes
TEST_F(HintManagerTest, ParseNodesTest) {
std::vector<std::unique_ptr<Node>> nodes =
HintManager::ParseNodes(json_doc_);
- EXPECT_EQ(2u, nodes.size());
+ 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());
@@ -237,11 +288,19 @@ TEST_F(HintManagerTest, ParseNodesTest) {
EXPECT_EQ(2u, nodes[1]->GetDefaultIndex());
EXPECT_TRUE(nodes[0]->GetResetOnInit());
EXPECT_FALSE(nodes[1]->GetResetOnInit());
- EXPECT_FALSE(nodes[0]->GetHoldFd());
- EXPECT_TRUE(nodes[1]->GetHoldFd());
+ // 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 actions
+// Test parsing nodes with duplicate name
TEST_F(HintManagerTest, ParseNodesDuplicateNameTest) {
std::string from = "CPUCluster0MinFreq";
size_t start_pos = json_doc_.find(from);
@@ -251,6 +310,15 @@ TEST_F(HintManagerTest, ParseNodesDuplicateNameTest) {
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;
@@ -262,7 +330,7 @@ TEST_F(HintManagerTest, ParseNodesDuplicatePathTest) {
}
// Test parsing invalid json for nodes
-TEST_F(HintManagerTest, ParseBadNodesTest) {
+TEST_F(HintManagerTest, ParseBadFileNodesTest) {
std::vector<std::unique_ptr<Node>> nodes =
HintManager::ParseNodes("invalid json");
EXPECT_EQ(0u, nodes.size());
@@ -279,24 +347,34 @@ TEST_F(HintManagerTest, ParseActionsTest) {
std::map<std::string, std::vector<NodeAction>> actions =
HintManager::ParseActions(json_doc_, nodes);
EXPECT_EQ(2u, actions.size());
- EXPECT_EQ(1u, actions["INTERACTION"].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["LAUNCH"].size());
+ 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(1u, actions["LAUNCH"][1].node_index);
+ EXPECT_EQ(2u, actions["LAUNCH"][1].node_index);
EXPECT_EQ(0u, actions["LAUNCH"][1].value_index);
- EXPECT_EQ(std::chrono::milliseconds(2000).count(),
+ 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 node
@@ -304,10 +382,10 @@ TEST_F(HintManagerTest, ParseActionDuplicateNodeTest) {
std::string from = "\"Node\":\"CPUCluster0MinFreq\"";
size_t start_pos = json_doc_.find(from);
json_doc_.replace(start_pos, from.length(),
- "\"Node\": \"CPUCluster1MinFreq\"");
+ "\"Node\":\"CPUCluster1MinFreq\"");
std::vector<std::unique_ptr<Node>> nodes =
HintManager::ParseNodes(json_doc_);
- EXPECT_EQ(2u, nodes.size());
+ EXPECT_EQ(3u, nodes.size());
std::map<std::string, std::vector<NodeAction>> actions =
HintManager::ParseActions(json_doc_, nodes);
EXPECT_EQ(0u, actions.size());
@@ -339,29 +417,35 @@ TEST_F(HintManagerTest, GetFromJSONTest) {
// 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
diff --git a/libperfmgr/tests/NodeLooperThreadTest.cc b/libperfmgr/tests/NodeLooperThreadTest.cc
index 24659cf7..9f36f896 100644
--- a/libperfmgr/tests/NodeLooperThreadTest.cc
+++ b/libperfmgr/tests/NodeLooperThreadTest.cc
@@ -22,6 +22,7 @@
#include <gtest/gtest.h>
+#include "perfmgr/FileNode.h"
#include "perfmgr/NodeLooperThread.h"
namespace android {
@@ -35,14 +36,14 @@ class NodeLooperThreadTest : public ::testing::Test {
protected:
virtual void SetUp() {
std::unique_ptr<TemporaryFile> tf = std::make_unique<TemporaryFile>();
- nodes_.emplace_back(
- new Node("n0", tf->path,
- {{"n0_value0"}, {"n0_value1"}, {"n0_value2"}}, 2, false));
+ 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 Node("n1", tf->path,
- {{"n1_value0"}, {"n1_value1"}, {"n1_value2"}}, 2, true));
+ nodes_.emplace_back(new FileNode(
+ "n1", tf->path, {{"n1_value0"}, {"n1_value1"}, {"n1_value2"}}, 2,
+ true));
files_.emplace_back(std::move(tf));
}
diff --git a/libperfmgr/tests/PropertyNodeTest.cc b/libperfmgr/tests/PropertyNodeTest.cc
new file mode 100644
index 00000000..925f76d2
--- /dev/null
+++ b/libperfmgr/tests/PropertyNodeTest.cc
@@ -0,0 +1,220 @@
+/*
+ * 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 <android-base/test_utils.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"}, {"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();
+ // 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();
+ _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();
+ _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();
+ _VerifyPropertyValue(key, "value2");
+ 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();
+ // 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();
+ _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();
+ _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();
+ _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();
+ // 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();
+ _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();
+ _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();
+ _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();
+ _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();
+ _VerifyPropertyValue(key, "value2");
+ EXPECT_EQ(std::chrono::milliseconds::max(), expire_time);
+}
+
+} // namespace perfmgr
+} // namespace android