profiling: Add management for system properties.
Change-Id: I6dea624542b3429086d4641a5fa91032a97709d1
diff --git a/Android.bp b/Android.bp
index 4eb1891..6e4f684 100644
--- a/Android.bp
+++ b/Android.bp
@@ -58,6 +58,7 @@
"src/profiling/memory/main.cc",
"src/profiling/memory/record_reader.cc",
"src/profiling/memory/socket_listener.cc",
+ "src/profiling/memory/system_property.cc",
"src/profiling/memory/unwinding.cc",
"src/profiling/memory/wire_protocol.cc",
"src/protozero/message.cc",
@@ -2299,6 +2300,8 @@
"src/profiling/memory/sampler_unittest.cc",
"src/profiling/memory/socket_listener.cc",
"src/profiling/memory/socket_listener_unittest.cc",
+ "src/profiling/memory/system_property.cc",
+ "src/profiling/memory/system_property_unittest.cc",
"src/profiling/memory/unwinding.cc",
"src/profiling/memory/unwinding_unittest.cc",
"src/profiling/memory/wire_protocol.cc",
diff --git a/src/profiling/memory/BUILD.gn b/src/profiling/memory/BUILD.gn
index 9514ea9..14745ac 100644
--- a/src/profiling/memory/BUILD.gn
+++ b/src/profiling/memory/BUILD.gn
@@ -63,6 +63,8 @@
"record_reader.h",
"socket_listener.cc",
"socket_listener.h",
+ "system_property.cc",
+ "system_property.h",
"unwinding.cc",
"unwinding.h",
]
@@ -106,6 +108,7 @@
"record_reader_unittest.cc",
"sampler_unittest.cc",
"socket_listener_unittest.cc",
+ "system_property_unittest.cc",
"unwinding_unittest.cc",
"wire_protocol_unittest.cc",
]
diff --git a/src/profiling/memory/system_property.cc b/src/profiling/memory/system_property.cc
new file mode 100644
index 0000000..499bc6b
--- /dev/null
+++ b/src/profiling/memory/system_property.cc
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/profiling/memory/system_property.h"
+
+#include "perfetto/base/logging.h"
+
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sys/system_properties.h>
+#endif
+
+namespace perfetto {
+namespace profiling {
+
+SystemProperties::Handle::Handle(Handle&& other) {
+ system_properties_ = other.system_properties_;
+ property_ = std::move(other.property_);
+ all_ = other.all_;
+ other.system_properties_ = nullptr;
+}
+
+SystemProperties::Handle& SystemProperties::Handle::operator=(Handle&& other) {
+ system_properties_ = other.system_properties_;
+ property_ = std::move(other.property_);
+ all_ = other.all_;
+ other.system_properties_ = nullptr;
+ return *this;
+}
+
+SystemProperties::Handle::Handle(SystemProperties* system_properties)
+ : system_properties_(system_properties), all_(true) {}
+
+SystemProperties::Handle::Handle(SystemProperties* system_properties,
+ std::string property)
+ : system_properties_(system_properties), property_(std::move(property)) {}
+
+SystemProperties::Handle::~Handle() {
+ if (system_properties_) {
+ if (all_)
+ system_properties_->UnsetAll();
+ else
+ system_properties_->UnsetProperty(property_);
+ }
+}
+
+SystemProperties::Handle::operator bool() {
+ return system_properties_ != nullptr;
+}
+
+SystemProperties::Handle SystemProperties::SetProperty(std::string name) {
+ auto it = properties_.find(name);
+ if (it == properties_.end()) {
+ if (!SetAndroidProperty("heapprofd.enable." + name, "1"))
+ return Handle(nullptr);
+ if (properties_.size() == 1 || alls_ == 0) {
+ if (!SetAndroidProperty("heapprofd.enable", "1"))
+ return Handle(nullptr);
+ }
+ properties_.emplace(name, 1);
+ } else {
+ it->second++;
+ }
+ return Handle(this, std::move(name));
+}
+
+SystemProperties::Handle SystemProperties::SetAll() {
+ if (alls_ == 0) {
+ if (!SetAndroidProperty("heapprofd.enable", "all"))
+ return Handle(nullptr);
+ }
+ alls_++;
+ return Handle(this);
+}
+
+SystemProperties::~SystemProperties() {
+ PERFETTO_DCHECK(alls_ == 0 && properties_.empty());
+}
+
+bool SystemProperties::SetAndroidProperty(const std::string& name,
+ const std::string& value) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ return __system_property_set(name.c_str(), value.c_str());
+#else
+ // Allow this to be mocked out for tests on other platforms.
+ base::ignore_result(name);
+ base::ignore_result(value);
+ PERFETTO_FATAL("Properties can only be set on Android.");
+#endif
+}
+
+void SystemProperties::UnsetProperty(const std::string& name) {
+ auto it = properties_.find(name);
+ if (it == properties_.end()) {
+ PERFETTO_DFATAL("Unsetting unknown property.");
+ return;
+ }
+ if (--(it->second) == 0) {
+ properties_.erase(it);
+ SetAndroidProperty("heapprofd.enable." + name, "");
+ if (properties_.empty() && alls_ == 0)
+ SetAndroidProperty("heapprofd.enable", "");
+ }
+}
+
+void SystemProperties::UnsetAll() {
+ if (--alls_ == 0) {
+ if (properties_.empty())
+ SetAndroidProperty("heapprofd.enable", "");
+ else
+ SetAndroidProperty("heapprofd.enable", "1");
+ }
+}
+
+} // namespace profiling
+} // namespace perfetto
diff --git a/src/profiling/memory/system_property.h b/src/profiling/memory/system_property.h
new file mode 100644
index 0000000..5bb6e8f
--- /dev/null
+++ b/src/profiling/memory/system_property.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_PROFILING_MEMORY_SYSTEM_PROPERTY_H_
+#define SRC_PROFILING_MEMORY_SYSTEM_PROPERTY_H_
+
+#include <map>
+#include <string>
+
+namespace perfetto {
+namespace profiling {
+
+// SystemProperties allows to set properties in a reference counted fashion.
+// SetAll() is used to enable startup profiling for all programs, SetProperty
+// can be used to enable startup profiling for a specific program name.
+// Both of those return opaque Handles that need to be held on to as long as
+// startup profiling should be enabled.
+//
+// This automatically manages the heappprofd.enable flag, which is first
+// checked to determine whether to check the program name specific flag.
+// Once the last Handle for a given program name goes away, the flag for the
+// program name is unset. Once the last of all Handles goes away, the
+// heapprofd.enable flag is unset.
+// See
+// https://android.googlesource.com/platform/bionic/+/0dbe6d1aec12d2f30f0331dcfea6dc8e8c55cf97/libc/bionic/malloc_common.cpp#473
+class SystemProperties {
+ public:
+ class Handle {
+ public:
+ Handle(const Handle&) = delete;
+ Handle& operator=(const Handle&) = delete;
+
+ Handle(Handle&&);
+ Handle& operator=(Handle&&);
+
+ friend class SystemProperties;
+ ~Handle();
+ operator bool();
+
+ private:
+ explicit Handle(SystemProperties* system_properties, std::string property);
+ explicit Handle(SystemProperties* system_properties);
+
+ SystemProperties* system_properties_;
+ std::string property_;
+ bool all_ = false;
+ };
+
+ Handle SetProperty(std::string name);
+ Handle SetAll();
+
+ virtual ~SystemProperties();
+
+ protected:
+ // virtual for testing.
+ virtual bool SetAndroidProperty(const std::string& name,
+ const std::string& value);
+
+ private:
+ void UnsetProperty(const std::string& name);
+ void UnsetAll();
+
+ size_t alls_ = 0;
+ std::map<std::string, size_t> properties_;
+};
+
+} // namespace profiling
+} // namespace perfetto
+
+#endif // SRC_PROFILING_MEMORY_SYSTEM_PROPERTY_H_
diff --git a/src/profiling/memory/system_property_unittest.cc b/src/profiling/memory/system_property_unittest.cc
new file mode 100644
index 0000000..2bf16ab
--- /dev/null
+++ b/src/profiling/memory/system_property_unittest.cc
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/profiling/memory/system_property.h"
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace perfetto {
+namespace profiling {
+namespace {
+
+using ::testing::InSequence;
+using ::testing::Return;
+
+class MockSystemProperties : public SystemProperties {
+ public:
+ MOCK_METHOD2(SetAndroidProperty,
+ bool(const std::string&, const std::string&));
+};
+
+TEST(SystemPropertyTest, All) {
+ MockSystemProperties prop;
+ InSequence s;
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable", "all"))
+ .WillOnce(Return(true));
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable", ""))
+ .WillOnce(Return(true));
+ auto handle = prop.SetAll();
+}
+
+TEST(SystemPropertyTest, RefcountAll) {
+ MockSystemProperties prop;
+ InSequence s;
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable", "all"))
+ .WillOnce(Return(true));
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable", ""))
+ .WillOnce(Return(true));
+ {
+ auto handle = prop.SetAll();
+ { auto handle2 = prop.SetAll(); }
+ }
+}
+
+TEST(SystemPropertyTest, CleanupAll) {
+ MockSystemProperties prop;
+ InSequence s;
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable", "all"))
+ .WillOnce(Return(true));
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable", ""))
+ .WillOnce(Return(true));
+ { auto handle = prop.SetAll(); }
+}
+
+TEST(SystemPropertyTest, Specific) {
+ MockSystemProperties prop;
+ InSequence s;
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable.system_server", "1"))
+ .WillOnce(Return(true));
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable", "1"))
+ .WillOnce(Return(true));
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable.system_server", ""))
+ .WillOnce(Return(true));
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable", ""))
+ .WillOnce(Return(true));
+ auto handle2 = prop.SetProperty("system_server");
+}
+
+TEST(SystemPropertyTest, RefcountSpecific) {
+ MockSystemProperties prop;
+ InSequence s;
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable.system_server", "1"))
+ .WillOnce(Return(true));
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable", "1"))
+ .WillOnce(Return(true));
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable.system_server", ""))
+ .WillOnce(Return(true));
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable", ""))
+ .WillOnce(Return(true));
+ {
+ auto handle = prop.SetProperty("system_server");
+ { auto handle2 = prop.SetProperty("system_server"); }
+ }
+}
+
+TEST(SystemPropertyTest, CleanupSpecific) {
+ MockSystemProperties prop;
+ InSequence s;
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable.system_server", "1"))
+ .WillOnce(Return(true));
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable", "1"))
+ .WillOnce(Return(true));
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable.system_server", ""))
+ .WillOnce(Return(true));
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable", ""))
+ .WillOnce(Return(true));
+ { auto handle2 = prop.SetProperty("system_server"); }
+}
+
+TEST(SystemPropertyTest, AllAndSpecific) {
+ MockSystemProperties prop;
+ InSequence s;
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable", "all"))
+ .WillOnce(Return(true));
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable.system_server", "1"))
+ .WillOnce(Return(true));
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable", "1"))
+ .WillOnce(Return(true));
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable.system_server", ""))
+ .WillOnce(Return(true));
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable", ""))
+ .WillOnce(Return(true));
+ auto handle = prop.SetAll();
+ auto handle2 = prop.SetProperty("system_server");
+ { SystemProperties::Handle destroy = std::move(handle); }
+}
+
+TEST(SystemPropertyTest, AllFailed) {
+ MockSystemProperties prop;
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable", "all"))
+ .WillOnce(Return(false));
+ auto handle = prop.SetAll();
+ EXPECT_FALSE(handle);
+}
+
+TEST(SystemPropertyTest, SpecificFailed) {
+ MockSystemProperties prop;
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable.system_server", "1"))
+ .WillOnce(Return(false));
+ auto handle = prop.SetProperty("system_server");
+ EXPECT_FALSE(handle);
+}
+
+TEST(SystemPropertyTest, SpecificFailedMainProperty) {
+ MockSystemProperties prop;
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable.system_server", "1"))
+ .WillOnce(Return(true));
+ EXPECT_CALL(prop, SetAndroidProperty("heapprofd.enable", "1"))
+ .WillOnce(Return(false));
+ auto handle = prop.SetProperty("system_server");
+ EXPECT_FALSE(handle);
+}
+
+} // namespace
+} // namespace profiling
+} // namespace perfetto