/*
* 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) {
// Construct this temporary because the RHS could be an lvalue cast to an
// rvalue reference whose lifetime we do not know.
Handle tmp(std::move(other));
using std::swap;
swap(*this, tmp);
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);
}
// This is conditionally noreturn, so disable the warning.
#pragma GCC diagnostic push
#if PERFETTO_DCHECK_IS_ON()
#pragma GCC diagnostic ignored "-Wmissing-noreturn"
#endif
// static
void SystemProperties::ResetHeapprofdProperties() {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
int r = __system_property_foreach(
[](const prop_info* pi, void*) {
__system_property_read_callback(
pi,
[](void*, const char* name, const char*, uint32_t) {
constexpr char kDebugModePropName[] = "heapprofd.userdebug.mode";
// Unset everything starting with "heapprofd.", except for the
// property stating which mode to use on debug builds.
const char* found = strstr(name, "heapprofd.");
if (found == name && strncmp(name, kDebugModePropName,
strlen(kDebugModePropName))) {
int ret = __system_property_set(name, "");
PERFETTO_DCHECK(ret == 0);
}
},
nullptr);
},
nullptr);
PERFETTO_DCHECK(r == 0);
#else
PERFETTO_DFATAL("Cannot ResetHeapprofdProperties on out-of-tree builds.");
#endif
}
#pragma GCC diagnostic pop
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()) == 0;
#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");
}
}
void swap(SystemProperties::Handle& a, SystemProperties::Handle& b) {
using std::swap;
swap(a.system_properties_, b.system_properties_);
swap(a.property_, b.property_);
swap(a.all_, b.all_);
}
} // namespace profiling
} // namespace perfetto