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