| /* |
| * 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 |