/* * 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 #include #include #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 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( ds_config_.tracing_session_id() % std::numeric_limits::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)); } 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 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 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