1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "src/profiling/memory/system_property.h"
18
19 #include "perfetto/base/logging.h"
20 #include "perfetto/ext/base/utils.h"
21
22 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
23 #include <sys/system_properties.h>
24 #endif
25
26 namespace perfetto {
27 namespace profiling {
28
Handle(Handle && other)29 SystemProperties::Handle::Handle(Handle&& other) noexcept {
30 system_properties_ = other.system_properties_;
31 property_ = std::move(other.property_);
32 all_ = other.all_;
33 other.system_properties_ = nullptr;
34 }
35
operator =(Handle && other)36 SystemProperties::Handle& SystemProperties::Handle::operator=(
37 Handle&& other) noexcept {
38 // Construct this temporary because the RHS could be an lvalue cast to an
39 // rvalue reference whose lifetime we do not know.
40 Handle tmp(std::move(other));
41 using std::swap;
42 swap(*this, tmp);
43 return *this;
44 }
45
Handle(SystemProperties * system_properties)46 SystemProperties::Handle::Handle(SystemProperties* system_properties)
47 : system_properties_(system_properties), all_(true) {}
48
Handle(SystemProperties * system_properties,std::string property)49 SystemProperties::Handle::Handle(SystemProperties* system_properties,
50 std::string property)
51 : system_properties_(system_properties), property_(std::move(property)) {}
52
~Handle()53 SystemProperties::Handle::~Handle() {
54 if (system_properties_) {
55 if (all_)
56 system_properties_->UnsetAll();
57 else
58 system_properties_->UnsetProperty(property_);
59 }
60 }
61
operator bool()62 SystemProperties::Handle::operator bool() {
63 return system_properties_ != nullptr;
64 }
65
SetProperty(std::string name)66 SystemProperties::Handle SystemProperties::SetProperty(std::string name) {
67 auto it = properties_.find(name);
68 if (it == properties_.end()) {
69 if (!SetAndroidProperty("heapprofd.enable." + name, "1"))
70 return Handle(nullptr);
71 if (properties_.size() == 1 || alls_ == 0) {
72 if (!SetAndroidProperty("heapprofd.enable", "1"))
73 return Handle(nullptr);
74 }
75 properties_.emplace(name, 1);
76 } else {
77 it->second++;
78 }
79 return Handle(this, std::move(name));
80 }
81
SetAll()82 SystemProperties::Handle SystemProperties::SetAll() {
83 if (alls_ == 0) {
84 if (!SetAndroidProperty("heapprofd.enable", "all"))
85 return Handle(nullptr);
86 }
87 alls_++;
88 return Handle(this);
89 }
90
91 // This is conditionally noreturn, so disable the warning.
92 #pragma GCC diagnostic push
93 #if PERFETTO_DCHECK_IS_ON()
94 #pragma GCC diagnostic ignored "-Wmissing-noreturn"
95 #endif
96
97 // static
ResetHeapprofdProperties()98 void SystemProperties::ResetHeapprofdProperties() {
99 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
100 int r = __system_property_foreach(
101 [](const prop_info* pi, void*) {
102 __system_property_read_callback(
103 pi,
104 [](void*, const char* name, const char*, uint32_t) {
105 constexpr char kDebugModePropName[] = "heapprofd.userdebug.mode";
106
107 // Unset everything starting with "heapprofd.", except for the
108 // property stating which mode to use on debug builds.
109 const char* found = strstr(name, "heapprofd.");
110 if (found == name && strncmp(name, kDebugModePropName,
111 strlen(kDebugModePropName))) {
112 int ret = __system_property_set(name, "");
113 PERFETTO_DCHECK(ret == 0);
114 }
115 },
116 nullptr);
117 },
118 nullptr);
119 PERFETTO_DCHECK(r == 0);
120 #else
121 PERFETTO_DFATAL_OR_ELOG(
122 "Cannot ResetHeapprofdProperties on out-of-tree builds.");
123 #endif
124 }
125
126 #pragma GCC diagnostic pop
127
~SystemProperties()128 SystemProperties::~SystemProperties() {
129 PERFETTO_DCHECK(alls_ == 0 && properties_.empty());
130 }
131
SetAndroidProperty(const std::string & name,const std::string & value)132 bool SystemProperties::SetAndroidProperty(const std::string& name,
133 const std::string& value) {
134 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
135 return __system_property_set(name.c_str(), value.c_str()) == 0;
136 #else
137 // Allow this to be mocked out for tests on other platforms.
138 base::ignore_result(name);
139 base::ignore_result(value);
140 PERFETTO_FATAL("Properties can only be set on Android.");
141 #endif
142 }
143
UnsetProperty(const std::string & name)144 void SystemProperties::UnsetProperty(const std::string& name) {
145 auto it = properties_.find(name);
146 if (it == properties_.end()) {
147 PERFETTO_DFATAL_OR_ELOG("Unsetting unknown property.");
148 return;
149 }
150 if (--(it->second) == 0) {
151 properties_.erase(it);
152 SetAndroidProperty("heapprofd.enable." + name, "");
153 if (properties_.empty() && alls_ == 0)
154 SetAndroidProperty("heapprofd.enable", "");
155 }
156 }
157
UnsetAll()158 void SystemProperties::UnsetAll() {
159 if (--alls_ == 0) {
160 if (properties_.empty())
161 SetAndroidProperty("heapprofd.enable", "");
162 else
163 SetAndroidProperty("heapprofd.enable", "1");
164 }
165 }
166
swap(SystemProperties::Handle & a,SystemProperties::Handle & b)167 void swap(SystemProperties::Handle& a, SystemProperties::Handle& b) {
168 using std::swap;
169 swap(a.system_properties_, b.system_properties_);
170 swap(a.property_, b.property_);
171 swap(a.all_, b.all_);
172 }
173
174 } // namespace profiling
175 } // namespace perfetto
176