xref: /aosp_15_r20/external/perfetto/src/profiling/memory/java_hprof_producer.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1 /*
2  * Copyright (C) 2019 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/java_hprof_producer.h"
18 
19 #include <signal.h>
20 #include <limits>
21 #include <optional>
22 
23 #include "perfetto/ext/tracing/core/trace_writer.h"
24 #include "src/profiling/common/proc_cmdline.h"
25 #include "src/profiling/common/proc_utils.h"
26 #include "src/profiling/common/producer_support.h"
27 
28 namespace perfetto {
29 namespace profiling {
30 namespace {
31 
32 constexpr int kJavaHeapprofdSignal = __SIGRTMIN + 6;
33 constexpr uint32_t kInitialConnectionBackoffMs = 100;
34 constexpr uint32_t kMaxConnectionBackoffMs = 30 * 1000;
35 constexpr const char* kJavaHprofDataSource = "android.java_hprof";
36 
37 }  // namespace
38 
DoContinuousDump(DataSourceInstanceID id,uint32_t dump_interval)39 void JavaHprofProducer::DoContinuousDump(DataSourceInstanceID id,
40                                          uint32_t dump_interval) {
41   auto it = data_sources_.find(id);
42   if (it == data_sources_.end())
43     return;
44   DataSource& ds = it->second;
45   if (!ds.config().continuous_dump_config().scan_pids_only_on_start()) {
46     ds.CollectPids();
47   }
48   ds.SendSignal();
49   auto weak_producer = weak_factory_.GetWeakPtr();
50   task_runner_->PostDelayedTask(
51       [weak_producer, id, dump_interval] {
52         if (!weak_producer)
53           return;
54         weak_producer->DoContinuousDump(id, dump_interval);
55       },
56       dump_interval);
57 }
58 
DataSource(DataSourceConfig ds_config,JavaHprofConfig config,std::vector<std::string> target_cmdlines)59 JavaHprofProducer::DataSource::DataSource(
60     DataSourceConfig ds_config,
61     JavaHprofConfig config,
62     std::vector<std::string> target_cmdlines)
63     : ds_config_(std::move(ds_config)),
64       config_(std::move(config)),
65       target_cmdlines_(std::move(target_cmdlines)) {}
66 
SendSignal() const67 void JavaHprofProducer::DataSource::SendSignal() const {
68   for (pid_t pid : pids_) {
69     auto opt_status = ReadStatus(pid);
70     if (!opt_status) {
71       PERFETTO_PLOG("Failed to read /proc/%d/status. Not signalling.", pid);
72       continue;
73     }
74     auto uids = GetUids(*opt_status);
75     if (!uids) {
76       PERFETTO_ELOG(
77           "Failed to read Uid from /proc/%d/status. "
78           "Not signalling.",
79           pid);
80       continue;
81     }
82     if (!CanProfile(ds_config_, uids->effective,
83                     config_.target_installed_by())) {
84       PERFETTO_ELOG("%d (UID %" PRIu64 ") not profileable.", pid,
85                     uids->effective);
86       continue;
87     }
88     PERFETTO_DLOG("Sending %d to %d", kJavaHeapprofdSignal, pid);
89     union sigval signal_value;
90     signal_value.sival_int = static_cast<int32_t>(
91         ds_config_.tracing_session_id() % std::numeric_limits<int32_t>::max());
92     if (sigqueue(pid, kJavaHeapprofdSignal, signal_value) != 0) {
93       PERFETTO_DPLOG("sigqueue");
94     }
95   }
96 }
97 
CollectPids()98 void JavaHprofProducer::DataSource::CollectPids() {
99   pids_.clear();
100   for (uint64_t pid : config_.pid()) {
101     pids_.insert(static_cast<pid_t>(pid));
102   }
103   glob_aware::FindPidsForCmdlinePatterns(target_cmdlines_, &pids_);
104   if (config_.min_anonymous_memory_kb() > 0)
105     RemoveUnderAnonThreshold(config_.min_anonymous_memory_kb(), &pids_);
106 }
107 
IncreaseConnectionBackoff()108 void JavaHprofProducer::IncreaseConnectionBackoff() {
109   connection_backoff_ms_ *= 2;
110   if (connection_backoff_ms_ > kMaxConnectionBackoffMs)
111     connection_backoff_ms_ = kMaxConnectionBackoffMs;
112 }
113 
ResetConnectionBackoff()114 void JavaHprofProducer::ResetConnectionBackoff() {
115   connection_backoff_ms_ = kInitialConnectionBackoffMs;
116 }
117 
SetupDataSource(DataSourceInstanceID id,const DataSourceConfig & ds_config)118 void JavaHprofProducer::SetupDataSource(DataSourceInstanceID id,
119                                         const DataSourceConfig& ds_config) {
120   if (data_sources_.find(id) != data_sources_.end()) {
121     PERFETTO_DFATAL_OR_ELOG("Duplicate data source: %" PRIu64, id);
122     return;
123   }
124   JavaHprofConfig config;
125   config.ParseFromString(ds_config.java_hprof_config_raw());
126   std::vector<std::string> cmdline_patterns = config.process_cmdline();
127   DataSource ds(ds_config, std::move(config), std::move(cmdline_patterns));
128   ds.CollectPids();
129   data_sources_.emplace(id, ds);
130 }
131 
StartDataSource(DataSourceInstanceID id,const DataSourceConfig &)132 void JavaHprofProducer::StartDataSource(DataSourceInstanceID id,
133                                         const DataSourceConfig&) {
134   auto it = data_sources_.find(id);
135   if (it == data_sources_.end()) {
136     PERFETTO_DFATAL_OR_ELOG("Starting invalid data source: %" PRIu64, id);
137     return;
138   }
139   const DataSource& ds = it->second;
140   const auto& continuous_dump_config = ds.config().continuous_dump_config();
141   uint32_t dump_interval = continuous_dump_config.dump_interval_ms();
142   if (dump_interval) {
143     auto weak_producer = weak_factory_.GetWeakPtr();
144     task_runner_->PostDelayedTask(
145         [weak_producer, id, dump_interval] {
146           if (!weak_producer)
147             return;
148           weak_producer->DoContinuousDump(id, dump_interval);
149         },
150         continuous_dump_config.dump_phase_ms());
151   }
152   ds.SendSignal();
153 }
154 
StopDataSource(DataSourceInstanceID id)155 void JavaHprofProducer::StopDataSource(DataSourceInstanceID id) {
156   auto it = data_sources_.find(id);
157   if (it == data_sources_.end()) {
158     PERFETTO_DFATAL_OR_ELOG("Stopping invalid data source: %" PRIu64, id);
159     return;
160   }
161   data_sources_.erase(it);
162 }
163 
Flush(FlushRequestID flush_id,const DataSourceInstanceID *,size_t,FlushFlags)164 void JavaHprofProducer::Flush(FlushRequestID flush_id,
165                               const DataSourceInstanceID*,
166                               size_t,
167                               FlushFlags) {
168   endpoint_->NotifyFlushComplete(flush_id);
169 }
170 
OnConnect()171 void JavaHprofProducer::OnConnect() {
172   PERFETTO_DCHECK(state_ == kConnecting);
173   state_ = kConnected;
174   ResetConnectionBackoff();
175   PERFETTO_LOG("Connected to the service.");
176 
177   DataSourceDescriptor desc;
178   desc.set_name(kJavaHprofDataSource);
179   endpoint_->RegisterDataSource(desc);
180 }
181 
Restart()182 void JavaHprofProducer::Restart() {
183   // We lost the connection with the tracing service. At this point we need
184   // to reset all the data sources. Trying to handle that manually is going to
185   // be error prone. What we do here is simply destroy the instance and
186   // recreate it again.
187   base::TaskRunner* task_runner = task_runner_;
188   const char* socket_name = producer_sock_name_;
189 
190   // Invoke destructor and then the constructor again.
191   this->~JavaHprofProducer();
192   new (this) JavaHprofProducer(task_runner);
193 
194   ConnectWithRetries(socket_name);
195 }
196 
ConnectWithRetries(const char * socket_name)197 void JavaHprofProducer::ConnectWithRetries(const char* socket_name) {
198   PERFETTO_DCHECK(state_ == kNotStarted);
199   state_ = kNotConnected;
200 
201   ResetConnectionBackoff();
202   producer_sock_name_ = socket_name;
203   ConnectService();
204 }
205 
SetProducerEndpoint(std::unique_ptr<TracingService::ProducerEndpoint> endpoint)206 void JavaHprofProducer::SetProducerEndpoint(
207     std::unique_ptr<TracingService::ProducerEndpoint> endpoint) {
208   PERFETTO_DCHECK(state_ == kNotConnected || state_ == kNotStarted);
209   state_ = kConnecting;
210   endpoint_ = std::move(endpoint);
211 }
212 
ConnectService()213 void JavaHprofProducer::ConnectService() {
214   SetProducerEndpoint(ProducerIPCClient::Connect(
215       producer_sock_name_, this, "android.java_hprof", task_runner_));
216 }
217 
OnDisconnect()218 void JavaHprofProducer::OnDisconnect() {
219   PERFETTO_DCHECK(state_ == kConnected || state_ == kConnecting);
220   PERFETTO_LOG("Disconnected from tracing service");
221 
222   auto weak_producer = weak_factory_.GetWeakPtr();
223   if (state_ == kConnected)
224     return task_runner_->PostTask([weak_producer] {
225       if (!weak_producer)
226         return;
227       weak_producer->Restart();
228     });
229 
230   state_ = kNotConnected;
231   IncreaseConnectionBackoff();
232   task_runner_->PostDelayedTask(
233       [weak_producer] {
234         if (!weak_producer)
235           return;
236         weak_producer->ConnectService();
237       },
238       connection_backoff_ms_);
239 }
240 
241 }  // namespace profiling
242 }  // namespace perfetto
243