blob: 01bd01c22822715756c523efdb2703c5f54e064e [file] [log] [blame]
/*
* Copyright (C) 2019 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/java_hprof_producer.h"
#include <signal.h>
#include <limits>
#include <optional>
#include "perfetto/ext/tracing/core/trace_writer.h"
#include "src/profiling/common/proc_cmdline.h"
#include "src/profiling/common/proc_utils.h"
#include "src/profiling/common/producer_support.h"
namespace perfetto {
namespace profiling {
namespace {
constexpr int kJavaHeapprofdSignal = __SIGRTMIN + 6;
constexpr uint32_t kInitialConnectionBackoffMs = 100;
constexpr uint32_t kMaxConnectionBackoffMs = 30 * 1000;
constexpr const char* kJavaHprofDataSource = "android.java_hprof";
} // namespace
void JavaHprofProducer::DoContinuousDump(DataSourceInstanceID id,
uint32_t dump_interval) {
auto it = data_sources_.find(id);
if (it == data_sources_.end())
return;
DataSource& ds = it->second;
if (!ds.config().continuous_dump_config().scan_pids_only_on_start()) {
ds.CollectPids();
}
ds.SendSignal();
auto weak_producer = weak_factory_.GetWeakPtr();
task_runner_->PostDelayedTask(
[weak_producer, id, dump_interval] {
if (!weak_producer)
return;
weak_producer->DoContinuousDump(id, dump_interval);
},
dump_interval);
}
JavaHprofProducer::DataSource::DataSource(
DataSourceConfig ds_config,
JavaHprofConfig config,
std::vector<std::string> target_cmdlines)
: ds_config_(std::move(ds_config)),
config_(std::move(config)),
target_cmdlines_(std::move(target_cmdlines)) {}
void JavaHprofProducer::DataSource::SendSignal() const {
for (pid_t pid : pids_) {
auto opt_status = ReadStatus(pid);
if (!opt_status) {
PERFETTO_PLOG("Failed to read /proc/%d/status. Not signalling.", pid);
continue;
}
auto uids = GetUids(*opt_status);
if (!uids) {
PERFETTO_ELOG(
"Failed to read Uid from /proc/%d/status. "
"Not signalling.",
pid);
continue;
}
if (!CanProfile(ds_config_, uids->effective,
config_.target_installed_by())) {
PERFETTO_ELOG("%d (UID %" PRIu64 ") not profileable.", pid,
uids->effective);
continue;
}
PERFETTO_DLOG("Sending %d to %d", kJavaHeapprofdSignal, pid);
union sigval signal_value;
signal_value.sival_int = static_cast<int32_t>(
ds_config_.tracing_session_id() % std::numeric_limits<int32_t>::max());
if (sigqueue(pid, kJavaHeapprofdSignal, signal_value) != 0) {
PERFETTO_DPLOG("sigqueue");
}
}
}
void JavaHprofProducer::DataSource::CollectPids() {
pids_.clear();
for (uint64_t pid : config_.pid()) {
pids_.insert(static_cast<pid_t>(pid));
}
glob_aware::FindPidsForCmdlinePatterns(target_cmdlines_, &pids_);
if (config_.min_anonymous_memory_kb() > 0)
RemoveUnderAnonThreshold(config_.min_anonymous_memory_kb(), &pids_);
}
void JavaHprofProducer::IncreaseConnectionBackoff() {
connection_backoff_ms_ *= 2;
if (connection_backoff_ms_ > kMaxConnectionBackoffMs)
connection_backoff_ms_ = kMaxConnectionBackoffMs;
}
void JavaHprofProducer::ResetConnectionBackoff() {
connection_backoff_ms_ = kInitialConnectionBackoffMs;
}
void JavaHprofProducer::SetupDataSource(DataSourceInstanceID id,
const DataSourceConfig& ds_config) {
if (data_sources_.find(id) != data_sources_.end()) {
PERFETTO_DFATAL_OR_ELOG("Duplicate data source: %" PRIu64, id);
return;
}
JavaHprofConfig config;
config.ParseFromString(ds_config.java_hprof_config_raw());
std::vector<std::string> cmdline_patterns = config.process_cmdline();
DataSource ds(ds_config, std::move(config), std::move(cmdline_patterns));
ds.CollectPids();
data_sources_.emplace(id, ds);
}
void JavaHprofProducer::StartDataSource(DataSourceInstanceID id,
const DataSourceConfig&) {
auto it = data_sources_.find(id);
if (it == data_sources_.end()) {
PERFETTO_DFATAL_OR_ELOG("Starting invalid data source: %" PRIu64, id);
return;
}
const DataSource& ds = it->second;
const auto& continuous_dump_config = ds.config().continuous_dump_config();
uint32_t dump_interval = continuous_dump_config.dump_interval_ms();
if (dump_interval) {
auto weak_producer = weak_factory_.GetWeakPtr();
task_runner_->PostDelayedTask(
[weak_producer, id, dump_interval] {
if (!weak_producer)
return;
weak_producer->DoContinuousDump(id, dump_interval);
},
continuous_dump_config.dump_phase_ms());
}
ds.SendSignal();
}
void JavaHprofProducer::StopDataSource(DataSourceInstanceID id) {
auto it = data_sources_.find(id);
if (it == data_sources_.end()) {
PERFETTO_DFATAL_OR_ELOG("Stopping invalid data source: %" PRIu64, id);
return;
}
data_sources_.erase(it);
}
void JavaHprofProducer::Flush(FlushRequestID flush_id,
const DataSourceInstanceID*,
size_t) {
endpoint_->NotifyFlushComplete(flush_id);
}
void JavaHprofProducer::OnConnect() {
PERFETTO_DCHECK(state_ == kConnecting);
state_ = kConnected;
ResetConnectionBackoff();
PERFETTO_LOG("Connected to the service.");
DataSourceDescriptor desc;
desc.set_name(kJavaHprofDataSource);
endpoint_->RegisterDataSource(desc);
}
void JavaHprofProducer::Restart() {
// We lost the connection with the tracing service. At this point we need
// to reset all the data sources. Trying to handle that manually is going to
// be error prone. What we do here is simply destroy the instance and
// recreate it again.
base::TaskRunner* task_runner = task_runner_;
const char* socket_name = producer_sock_name_;
// Invoke destructor and then the constructor again.
this->~JavaHprofProducer();
new (this) JavaHprofProducer(task_runner);
ConnectWithRetries(socket_name);
}
void JavaHprofProducer::ConnectWithRetries(const char* socket_name) {
PERFETTO_DCHECK(state_ == kNotStarted);
state_ = kNotConnected;
ResetConnectionBackoff();
producer_sock_name_ = socket_name;
ConnectService();
}
void JavaHprofProducer::SetProducerEndpoint(
std::unique_ptr<TracingService::ProducerEndpoint> endpoint) {
PERFETTO_DCHECK(state_ == kNotConnected || state_ == kNotStarted);
state_ = kConnecting;
endpoint_ = std::move(endpoint);
}
void JavaHprofProducer::ConnectService() {
SetProducerEndpoint(ProducerIPCClient::Connect(
producer_sock_name_, this, "android.java_hprof", task_runner_));
}
void JavaHprofProducer::OnDisconnect() {
PERFETTO_DCHECK(state_ == kConnected || state_ == kConnecting);
PERFETTO_LOG("Disconnected from tracing service");
auto weak_producer = weak_factory_.GetWeakPtr();
if (state_ == kConnected)
return task_runner_->PostTask([weak_producer] {
if (!weak_producer)
return;
weak_producer->Restart();
});
state_ = kNotConnected;
IncreaseConnectionBackoff();
task_runner_->PostDelayedTask(
[weak_producer] {
if (!weak_producer)
return;
weak_producer->ConnectService();
},
connection_backoff_ms_);
}
} // namespace profiling
} // namespace perfetto