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
21 #include "perfetto/ext/base/optional.h"
22 #include "perfetto/ext/tracing/core/trace_writer.h"
23 #include "src/profiling/common/proc_utils.h"
24
25 namespace perfetto {
26 namespace profiling {
27 namespace {
28
29 constexpr int kJavaHeapprofdSignal = __SIGRTMIN + 6;
30 constexpr uint32_t kInitialConnectionBackoffMs = 100;
31 constexpr uint32_t kMaxConnectionBackoffMs = 30 * 1000;
32 constexpr const char* kJavaHprofDataSource = "android.java_hprof";
33
34 } // namespace
35
DoContinuousDump(DataSourceInstanceID id,uint32_t dump_interval)36 void JavaHprofProducer::DoContinuousDump(DataSourceInstanceID id,
37 uint32_t dump_interval) {
38 auto it = data_sources_.find(id);
39 if (it == data_sources_.end())
40 return;
41 const DataSource& ds = it->second;
42 SignalDataSource(ds);
43 auto weak_producer = weak_factory_.GetWeakPtr();
44 task_runner_->PostDelayedTask(
45 [weak_producer, id, dump_interval] {
46 if (!weak_producer)
47 return;
48 weak_producer->DoContinuousDump(id, dump_interval);
49 },
50 dump_interval);
51 }
52
53 // static
SignalDataSource(const DataSource & ds)54 void JavaHprofProducer::SignalDataSource(const DataSource& ds) {
55 const std::set<pid_t>& pids = ds.pids;
56 for (pid_t pid : pids) {
57 PERFETTO_DLOG("Sending %d to %d", kJavaHeapprofdSignal, pid);
58 if (kill(pid, kJavaHeapprofdSignal) != 0) {
59 PERFETTO_DPLOG("kill");
60 }
61 }
62 }
63
IncreaseConnectionBackoff()64 void JavaHprofProducer::IncreaseConnectionBackoff() {
65 connection_backoff_ms_ *= 2;
66 if (connection_backoff_ms_ > kMaxConnectionBackoffMs)
67 connection_backoff_ms_ = kMaxConnectionBackoffMs;
68 }
69
ResetConnectionBackoff()70 void JavaHprofProducer::ResetConnectionBackoff() {
71 connection_backoff_ms_ = kInitialConnectionBackoffMs;
72 }
73
SetupDataSource(DataSourceInstanceID id,const DataSourceConfig & ds_config)74 void JavaHprofProducer::SetupDataSource(DataSourceInstanceID id,
75 const DataSourceConfig& ds_config) {
76 if (data_sources_.find(id) != data_sources_.end()) {
77 PERFETTO_DFATAL_OR_ELOG("Duplicate data source: %" PRIu64, id);
78 return;
79 }
80 JavaHprofConfig config;
81 config.ParseFromString(ds_config.java_hprof_config_raw());
82 DataSource ds;
83 ds.id = id;
84 for (uint64_t pid : config.pid())
85 ds.pids.emplace(static_cast<pid_t>(pid));
86 base::Optional<std::vector<std::string>> normalized_cmdlines =
87 NormalizeCmdlines(config.process_cmdline());
88 if (!normalized_cmdlines.has_value()) {
89 PERFETTO_ELOG("Rejecting data source due to invalid cmdline in config.");
90 return;
91 }
92 FindPidsForCmdlines(normalized_cmdlines.value(), &ds.pids);
93 if (config.min_anonymous_memory_kb() > 0)
94 RemoveUnderAnonThreshold(config.min_anonymous_memory_kb(), &ds.pids);
95
96 ds.config = std::move(config);
97 data_sources_.emplace(id, std::move(ds));
98 }
99
StartDataSource(DataSourceInstanceID id,const DataSourceConfig &)100 void JavaHprofProducer::StartDataSource(DataSourceInstanceID id,
101 const DataSourceConfig&) {
102 auto it = data_sources_.find(id);
103 if (it == data_sources_.end()) {
104 PERFETTO_DFATAL_OR_ELOG("Starting invalid data source: %" PRIu64, id);
105 return;
106 }
107 const DataSource& ds = it->second;
108 const auto continuous_dump_config = ds.config.continuous_dump_config();
109 uint32_t dump_interval = continuous_dump_config.dump_interval_ms();
110 if (dump_interval) {
111 auto weak_producer = weak_factory_.GetWeakPtr();
112 task_runner_->PostDelayedTask(
113 [weak_producer, id, dump_interval] {
114 if (!weak_producer)
115 return;
116 weak_producer->DoContinuousDump(id, dump_interval);
117 },
118 continuous_dump_config.dump_phase_ms());
119 }
120 SignalDataSource(ds);
121 }
122
StopDataSource(DataSourceInstanceID id)123 void JavaHprofProducer::StopDataSource(DataSourceInstanceID id) {
124 auto it = data_sources_.find(id);
125 if (it == data_sources_.end()) {
126 PERFETTO_DFATAL_OR_ELOG("Stopping invalid data source: %" PRIu64, id);
127 return;
128 }
129 data_sources_.erase(it);
130 }
131
Flush(FlushRequestID flush_id,const DataSourceInstanceID *,size_t)132 void JavaHprofProducer::Flush(FlushRequestID flush_id,
133 const DataSourceInstanceID*,
134 size_t) {
135 endpoint_->NotifyFlushComplete(flush_id);
136 }
137
OnConnect()138 void JavaHprofProducer::OnConnect() {
139 PERFETTO_DCHECK(state_ == kConnecting);
140 state_ = kConnected;
141 ResetConnectionBackoff();
142 PERFETTO_LOG("Connected to the service.");
143
144 DataSourceDescriptor desc;
145 desc.set_name(kJavaHprofDataSource);
146 endpoint_->RegisterDataSource(desc);
147 }
148
Restart()149 void JavaHprofProducer::Restart() {
150 // We lost the connection with the tracing service. At this point we need
151 // to reset all the data sources. Trying to handle that manually is going to
152 // be error prone. What we do here is simply destroy the instance and
153 // recreate it again.
154 base::TaskRunner* task_runner = task_runner_;
155 const char* socket_name = producer_sock_name_;
156
157 // Invoke destructor and then the constructor again.
158 this->~JavaHprofProducer();
159 new (this) JavaHprofProducer(task_runner);
160
161 ConnectWithRetries(socket_name);
162 }
163
ConnectWithRetries(const char * socket_name)164 void JavaHprofProducer::ConnectWithRetries(const char* socket_name) {
165 PERFETTO_DCHECK(state_ == kNotStarted);
166 state_ = kNotConnected;
167
168 ResetConnectionBackoff();
169 producer_sock_name_ = socket_name;
170 ConnectService();
171 }
172
SetProducerEndpoint(std::unique_ptr<TracingService::ProducerEndpoint> endpoint)173 void JavaHprofProducer::SetProducerEndpoint(
174 std::unique_ptr<TracingService::ProducerEndpoint> endpoint) {
175 PERFETTO_DCHECK(state_ == kNotConnected || state_ == kNotStarted);
176 state_ = kConnecting;
177 endpoint_ = std::move(endpoint);
178 }
179
ConnectService()180 void JavaHprofProducer::ConnectService() {
181 SetProducerEndpoint(ProducerIPCClient::Connect(
182 producer_sock_name_, this, "android.java_hprof", task_runner_));
183 }
184
OnDisconnect()185 void JavaHprofProducer::OnDisconnect() {
186 PERFETTO_DCHECK(state_ == kConnected || state_ == kConnecting);
187 PERFETTO_LOG("Disconnected from tracing service");
188
189 auto weak_producer = weak_factory_.GetWeakPtr();
190 if (state_ == kConnected)
191 return task_runner_->PostTask([weak_producer] {
192 if (!weak_producer)
193 return;
194 weak_producer->Restart();
195 });
196
197 state_ = kNotConnected;
198 IncreaseConnectionBackoff();
199 task_runner_->PostDelayedTask(
200 [weak_producer] {
201 if (!weak_producer)
202 return;
203 weak_producer->ConnectService();
204 },
205 connection_backoff_ms_);
206 }
207
208 } // namespace profiling
209 } // namespace perfetto
210