• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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