• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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/heapprofd_producer.h"
18 
19 #include <algorithm>
20 
21 #include <inttypes.h>
22 #include <signal.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 
27 #include "perfetto/ext/base/file_utils.h"
28 #include "perfetto/ext/base/optional.h"
29 #include "perfetto/ext/base/string_utils.h"
30 #include "perfetto/ext/base/thread_task_runner.h"
31 #include "perfetto/ext/base/watchdog_posix.h"
32 #include "perfetto/ext/tracing/core/trace_writer.h"
33 #include "perfetto/ext/tracing/ipc/producer_ipc_client.h"
34 #include "perfetto/tracing/core/data_source_config.h"
35 #include "perfetto/tracing/core/data_source_descriptor.h"
36 
37 namespace perfetto {
38 namespace profiling {
39 namespace {
40 using ::perfetto::protos::pbzero::ProfilePacket;
41 
42 constexpr char kHeapprofdDataSource[] = "android.heapprofd";
43 constexpr size_t kUnwinderThreads = 5;
44 
45 constexpr uint32_t kInitialConnectionBackoffMs = 100;
46 constexpr uint32_t kMaxConnectionBackoffMs = 30 * 1000;
47 constexpr uint32_t kGuardrailIntervalMs = 30 * 1000;
48 
49 constexpr uint32_t kChildModeWatchdogPeriodMs = 10 * 1000;
50 
51 constexpr uint64_t kDefaultShmemSize = 8 * 1048576;  // ~8 MB
52 constexpr uint64_t kMaxShmemSize = 500 * 1048576;    // ~500 MB
53 
54 // Constants specified by bionic, hardcoded here for simplicity.
55 constexpr int kProfilingSignal = __SIGRTMIN + 4;
56 constexpr int kHeapprofdSignalValue = 0;
57 
MakeUnwindingWorkers(HeapprofdProducer * delegate,size_t n)58 std::vector<UnwindingWorker> MakeUnwindingWorkers(HeapprofdProducer* delegate,
59                                                   size_t n) {
60   std::vector<UnwindingWorker> ret;
61   for (size_t i = 0; i < n; ++i) {
62     ret.emplace_back(delegate, base::ThreadTaskRunner::CreateAndStart());
63   }
64   return ret;
65 }
66 
ConfigTargetsProcess(const HeapprofdConfig & cfg,const Process & proc,const std::vector<std::string> & normalized_cmdlines)67 bool ConfigTargetsProcess(const HeapprofdConfig& cfg,
68                           const Process& proc,
69                           const std::vector<std::string>& normalized_cmdlines) {
70   if (cfg.all())
71     return true;
72 
73   const auto& pids = cfg.pid();
74   if (std::find(pids.cbegin(), pids.cend(), static_cast<uint64_t>(proc.pid)) !=
75       pids.cend()) {
76     return true;
77   }
78 
79   if (std::find(normalized_cmdlines.cbegin(), normalized_cmdlines.cend(),
80                 proc.cmdline) != normalized_cmdlines.cend()) {
81     return true;
82   }
83   return false;
84 }
85 
86 // Return largest n such that pow(2, n) < value.
Log2LessThan(uint64_t value)87 size_t Log2LessThan(uint64_t value) {
88   size_t i = 0;
89   while (value) {
90     i++;
91     value >>= 1;
92   }
93   return i;
94 }
95 
96 }  // namespace
97 
98 const uint64_t LogHistogram::kMaxBucket = 0;
99 
GetData()100 std::vector<std::pair<uint64_t, uint64_t>> LogHistogram::GetData() {
101   std::vector<std::pair<uint64_t, uint64_t>> data;
102   data.reserve(kBuckets);
103   for (size_t i = 0; i < kBuckets; ++i) {
104     if (i == kBuckets - 1)
105       data.emplace_back(kMaxBucket, values_[i]);
106     else
107       data.emplace_back(1 << i, values_[i]);
108   }
109   return data;
110 }
111 
GetBucket(uint64_t value)112 size_t LogHistogram::GetBucket(uint64_t value) {
113   if (value == 0)
114     return 0;
115 
116   size_t hibit = Log2LessThan(value);
117   if (hibit >= kBuckets)
118     return kBuckets - 1;
119   return hibit;
120 }
121 
122 // We create kUnwinderThreads unwinding threads. Bookkeeping is done on the main
123 // thread.
HeapprofdProducer(HeapprofdMode mode,base::TaskRunner * task_runner)124 HeapprofdProducer::HeapprofdProducer(HeapprofdMode mode,
125                                      base::TaskRunner* task_runner)
126     : task_runner_(task_runner),
127       mode_(mode),
128       unwinding_workers_(MakeUnwindingWorkers(this, kUnwinderThreads)),
129       socket_delegate_(this),
130       weak_factory_(this) {
131   CheckDataSourceMemory();  // Kick off guardrail task.
132   stat_fd_.reset(open("/proc/self/stat", O_RDONLY));
133   if (!stat_fd_) {
134     PERFETTO_ELOG(
135         "Failed to open /proc/self/stat. Cannot accept profiles "
136         "with CPU guardrails.");
137   } else {
138     CheckDataSourceCpu();  // Kick off guardrail task.
139   }
140 }
141 
142 HeapprofdProducer::~HeapprofdProducer() = default;
143 
SetTargetProcess(pid_t target_pid,std::string target_cmdline,base::ScopedFile inherited_socket)144 void HeapprofdProducer::SetTargetProcess(pid_t target_pid,
145                                          std::string target_cmdline,
146                                          base::ScopedFile inherited_socket) {
147   target_process_.pid = target_pid;
148   target_process_.cmdline = target_cmdline;
149   inherited_fd_ = std::move(inherited_socket);
150 }
151 
AdoptTargetProcessSocket()152 void HeapprofdProducer::AdoptTargetProcessSocket() {
153   PERFETTO_DCHECK(mode_ == HeapprofdMode::kChild);
154   auto socket = base::UnixSocket::AdoptConnected(
155       std::move(inherited_fd_), &socket_delegate_, task_runner_,
156       base::SockFamily::kUnix, base::SockType::kStream);
157 
158   HandleClientConnection(std::move(socket), target_process_);
159 }
160 
OnConnect()161 void HeapprofdProducer::OnConnect() {
162   PERFETTO_DCHECK(state_ == kConnecting);
163   state_ = kConnected;
164   ResetConnectionBackoff();
165   PERFETTO_LOG("Connected to the service, mode [%s].",
166                mode_ == HeapprofdMode::kCentral ? "central" : "child");
167 
168   DataSourceDescriptor desc;
169   desc.set_name(kHeapprofdDataSource);
170   desc.set_will_notify_on_stop(true);
171   endpoint_->RegisterDataSource(desc);
172 }
173 
OnDisconnect()174 void HeapprofdProducer::OnDisconnect() {
175   PERFETTO_DCHECK(state_ == kConnected || state_ == kConnecting);
176   PERFETTO_LOG("Disconnected from tracing service");
177 
178   // Do not attempt to reconnect if we're a process-private process, just quit.
179   if (mode_ == HeapprofdMode::kChild) {
180     TerminateProcess(/*exit_status=*/1);  // does not return
181   }
182 
183   // Central mode - attempt to reconnect.
184   auto weak_producer = weak_factory_.GetWeakPtr();
185   if (state_ == kConnected)
186     return task_runner_->PostTask([weak_producer] {
187       if (!weak_producer)
188         return;
189       weak_producer->Restart();
190     });
191 
192   state_ = kNotConnected;
193   IncreaseConnectionBackoff();
194   task_runner_->PostDelayedTask(
195       [weak_producer] {
196         if (!weak_producer)
197           return;
198         weak_producer->ConnectService();
199       },
200       connection_backoff_ms_);
201 }
202 
ConnectWithRetries(const char * socket_name)203 void HeapprofdProducer::ConnectWithRetries(const char* socket_name) {
204   PERFETTO_DCHECK(state_ == kNotStarted);
205   state_ = kNotConnected;
206 
207   ResetConnectionBackoff();
208   producer_sock_name_ = socket_name;
209   ConnectService();
210 }
211 
ConnectService()212 void HeapprofdProducer::ConnectService() {
213   SetProducerEndpoint(ProducerIPCClient::Connect(
214       producer_sock_name_, this, "android.heapprofd", task_runner_));
215 }
216 
SetProducerEndpoint(std::unique_ptr<TracingService::ProducerEndpoint> endpoint)217 void HeapprofdProducer::SetProducerEndpoint(
218     std::unique_ptr<TracingService::ProducerEndpoint> endpoint) {
219   PERFETTO_DCHECK(state_ == kNotConnected || state_ == kNotStarted);
220   state_ = kConnecting;
221   endpoint_ = std::move(endpoint);
222 }
223 
IncreaseConnectionBackoff()224 void HeapprofdProducer::IncreaseConnectionBackoff() {
225   connection_backoff_ms_ *= 2;
226   if (connection_backoff_ms_ > kMaxConnectionBackoffMs)
227     connection_backoff_ms_ = kMaxConnectionBackoffMs;
228 }
229 
ResetConnectionBackoff()230 void HeapprofdProducer::ResetConnectionBackoff() {
231   connection_backoff_ms_ = kInitialConnectionBackoffMs;
232 }
233 
Restart()234 void HeapprofdProducer::Restart() {
235   // We lost the connection with the tracing service. At this point we need
236   // to reset all the data sources. Trying to handle that manually is going to
237   // be error prone. What we do here is simply destroy the instance and
238   // recreate it again.
239 
240   // Child mode producer should not attempt restarts. Note that this also means
241   // the rest of this method doesn't have to handle child-specific state.
242   if (mode_ == HeapprofdMode::kChild)
243     PERFETTO_FATAL("Attempting to restart a child mode producer.");
244 
245   HeapprofdMode mode = mode_;
246   base::TaskRunner* task_runner = task_runner_;
247   const char* socket_name = producer_sock_name_;
248 
249   // Invoke destructor and then the constructor again.
250   this->~HeapprofdProducer();
251   new (this) HeapprofdProducer(mode, task_runner);
252 
253   ConnectWithRetries(socket_name);
254 }
255 
ScheduleActiveDataSourceWatchdog()256 void HeapprofdProducer::ScheduleActiveDataSourceWatchdog() {
257   PERFETTO_DCHECK(mode_ == HeapprofdMode::kChild);
258 
259   // Post the first check after a delay, to let the freshly forked heapprofd
260   // to receive the active data sources from traced. The checks will reschedule
261   // themselves from that point onwards.
262   auto weak_producer = weak_factory_.GetWeakPtr();
263   task_runner_->PostDelayedTask(
264       [weak_producer]() {
265         if (!weak_producer)
266           return;
267         weak_producer->ActiveDataSourceWatchdogCheck();
268       },
269       kChildModeWatchdogPeriodMs);
270 }
271 
ActiveDataSourceWatchdogCheck()272 void HeapprofdProducer::ActiveDataSourceWatchdogCheck() {
273   PERFETTO_DCHECK(mode_ == HeapprofdMode::kChild);
274 
275   // Fork mode heapprofd should be working on exactly one data source matching
276   // its target process.
277   if (data_sources_.empty()) {
278     PERFETTO_LOG(
279         "Child heapprofd exiting as it never received a data source for the "
280         "target process, or somehow lost/finished the task without exiting.");
281     TerminateProcess(/*exit_status=*/1);
282   } else {
283     // reschedule check.
284     auto weak_producer = weak_factory_.GetWeakPtr();
285     task_runner_->PostDelayedTask(
286         [weak_producer]() {
287           if (!weak_producer)
288             return;
289           weak_producer->ActiveDataSourceWatchdogCheck();
290         },
291         kChildModeWatchdogPeriodMs);
292   }
293 }
294 
295 // TODO(rsavitski): would be cleaner to shut down the event loop instead
296 // (letting main exit). One test-friendly approach is to supply a shutdown
297 // callback in the constructor.
TerminateProcess(int exit_status)298 __attribute__((noreturn)) void HeapprofdProducer::TerminateProcess(
299     int exit_status) {
300   PERFETTO_CHECK(mode_ == HeapprofdMode::kChild);
301   exit(exit_status);
302 }
303 
OnTracingSetup()304 void HeapprofdProducer::OnTracingSetup() {}
305 
SetupDataSource(DataSourceInstanceID id,const DataSourceConfig & ds_config)306 void HeapprofdProducer::SetupDataSource(DataSourceInstanceID id,
307                                         const DataSourceConfig& ds_config) {
308   PERFETTO_DLOG("Setting up data source.");
309   if (mode_ == HeapprofdMode::kChild && ds_config.enable_extra_guardrails()) {
310     PERFETTO_ELOG("enable_extra_guardrails is not supported on user.");
311     return;
312   }
313 
314   HeapprofdConfig heapprofd_config;
315   heapprofd_config.ParseFromString(ds_config.heapprofd_config_raw());
316 
317   if (heapprofd_config.all() && !heapprofd_config.pid().empty())
318     PERFETTO_ELOG("No point setting all and pid");
319   if (heapprofd_config.all() && !heapprofd_config.process_cmdline().empty())
320     PERFETTO_ELOG("No point setting all and process_cmdline");
321 
322   if (ds_config.name() != kHeapprofdDataSource) {
323     PERFETTO_DLOG("Invalid data source name.");
324     return;
325   }
326 
327   if (data_sources_.find(id) != data_sources_.end()) {
328     PERFETTO_DFATAL_OR_ELOG(
329         "Received duplicated data source instance id: %" PRIu64, id);
330     return;
331   }
332 
333   base::Optional<std::vector<std::string>> normalized_cmdlines =
334       NormalizeCmdlines(heapprofd_config.process_cmdline());
335   if (!normalized_cmdlines.has_value()) {
336     PERFETTO_ELOG("Rejecting data source due to invalid cmdline in config.");
337     return;
338   }
339 
340   // Child mode is only interested in the first data source matching the
341   // already-connected process.
342   if (mode_ == HeapprofdMode::kChild) {
343     if (!ConfigTargetsProcess(heapprofd_config, target_process_,
344                               normalized_cmdlines.value())) {
345       PERFETTO_DLOG("Child mode skipping setup of unrelated data source.");
346       return;
347     }
348 
349     if (!data_sources_.empty()) {
350       PERFETTO_LOG("Child mode skipping concurrent data source.");
351 
352       // Manually write one ProfilePacket about the rejected session.
353       auto buffer_id = static_cast<BufferID>(ds_config.target_buffer());
354       auto trace_writer = endpoint_->CreateTraceWriter(buffer_id);
355       auto trace_packet = trace_writer->NewTracePacket();
356       trace_packet->set_timestamp(
357           static_cast<uint64_t>(base::GetBootTimeNs().count()));
358       auto profile_packet = trace_packet->set_profile_packet();
359       auto process_dump = profile_packet->add_process_dumps();
360       process_dump->set_pid(static_cast<uint64_t>(target_process_.pid));
361       process_dump->set_rejected_concurrent(true);
362       trace_packet->Finalize();
363       trace_writer->Flush();
364       return;
365     }
366   }
367 
368   base::Optional<uint64_t> start_cputime_sec;
369   if (heapprofd_config.max_heapprofd_cpu_secs() > 0) {
370     start_cputime_sec = GetCputimeSec();
371 
372     if (!start_cputime_sec) {
373       PERFETTO_ELOG("Failed to enforce CPU guardrail. Rejecting config.");
374       return;
375     }
376   }
377 
378   auto buffer_id = static_cast<BufferID>(ds_config.target_buffer());
379   DataSource data_source(endpoint_->CreateTraceWriter(buffer_id));
380   data_source.id = id;
381   auto& cli_config = data_source.client_configuration;
382   cli_config.interval = heapprofd_config.sampling_interval_bytes();
383   cli_config.block_client = heapprofd_config.block_client();
384   cli_config.disable_fork_teardown = heapprofd_config.disable_fork_teardown();
385   cli_config.disable_vfork_detection =
386       heapprofd_config.disable_vfork_detection();
387   cli_config.block_client_timeout_us =
388       heapprofd_config.block_client_timeout_us();
389   data_source.config = heapprofd_config;
390   data_source.normalized_cmdlines = std::move(normalized_cmdlines.value());
391   data_source.stop_timeout_ms = ds_config.stop_timeout_ms();
392   data_source.start_cputime_sec = start_cputime_sec;
393 
394   InterningOutputTracker::WriteFixedInterningsPacket(
395       data_source.trace_writer.get());
396   data_sources_.emplace(id, std::move(data_source));
397   PERFETTO_DLOG("Set up data source.");
398 
399   if (mode_ == HeapprofdMode::kChild)
400     AdoptTargetProcessSocket();
401 }
402 
IsPidProfiled(pid_t pid)403 bool HeapprofdProducer::IsPidProfiled(pid_t pid) {
404   for (const auto& pair : data_sources_) {
405     const DataSource& ds = pair.second;
406     if (ds.process_states.find(pid) != ds.process_states.cend())
407       return true;
408   }
409   return false;
410 }
411 
GetCputimeSec()412 base::Optional<uint64_t> HeapprofdProducer::GetCputimeSec() {
413   if (!stat_fd_) {
414     return base::nullopt;
415   }
416   lseek(stat_fd_.get(), 0, SEEK_SET);
417   base::ProcStat stat;
418   if (!ReadProcStat(stat_fd_.get(), &stat)) {
419     PERFETTO_ELOG("Failed to read stat file to enforce guardrails.");
420     return base::nullopt;
421   }
422   return (stat.utime + stat.stime) /
423          static_cast<unsigned long>(sysconf(_SC_CLK_TCK));
424 }
425 
SetStartupProperties(DataSource * data_source)426 void HeapprofdProducer::SetStartupProperties(DataSource* data_source) {
427   const HeapprofdConfig& heapprofd_config = data_source->config;
428   if (heapprofd_config.all())
429     data_source->properties.emplace_back(properties_.SetAll());
430 
431   for (std::string cmdline : data_source->normalized_cmdlines)
432     data_source->properties.emplace_back(
433         properties_.SetProperty(std::move(cmdline)));
434 }
435 
SignalRunningProcesses(DataSource * data_source)436 void HeapprofdProducer::SignalRunningProcesses(DataSource* data_source) {
437   const HeapprofdConfig& heapprofd_config = data_source->config;
438 
439   std::set<pid_t> pids;
440   if (heapprofd_config.all())
441     FindAllProfilablePids(&pids);
442   for (uint64_t pid : heapprofd_config.pid())
443     pids.emplace(static_cast<pid_t>(pid));
444 
445   if (!data_source->normalized_cmdlines.empty())
446     FindPidsForCmdlines(data_source->normalized_cmdlines, &pids);
447 
448   if (heapprofd_config.min_anonymous_memory_kb() > 0)
449     RemoveUnderAnonThreshold(heapprofd_config.min_anonymous_memory_kb(), &pids);
450 
451   for (auto pid_it = pids.cbegin(); pid_it != pids.cend();) {
452     pid_t pid = *pid_it;
453     if (IsPidProfiled(pid)) {
454       PERFETTO_LOG("Rejecting concurrent session for %" PRIdMAX,
455                    static_cast<intmax_t>(pid));
456       data_source->rejected_pids.emplace(pid);
457       pid_it = pids.erase(pid_it);
458       continue;
459     }
460 
461     PERFETTO_DLOG("Sending signal: %d (si_value: %d) to pid: %d",
462                   kProfilingSignal, kHeapprofdSignalValue, pid);
463     union sigval signal_value;
464     signal_value.sival_int = kHeapprofdSignalValue;
465     if (sigqueue(pid, kProfilingSignal, signal_value) != 0) {
466       PERFETTO_DPLOG("sigqueue");
467     }
468     ++pid_it;
469   }
470   data_source->signaled_pids = std::move(pids);
471 }
472 
StartDataSource(DataSourceInstanceID id,const DataSourceConfig &)473 void HeapprofdProducer::StartDataSource(DataSourceInstanceID id,
474                                         const DataSourceConfig&) {
475   PERFETTO_DLOG("Start DataSource");
476 
477   auto it = data_sources_.find(id);
478   if (it == data_sources_.end()) {
479     // This is expected in child heapprofd, where we reject uninteresting data
480     // sources in SetupDataSource.
481     if (mode_ == HeapprofdMode::kCentral) {
482       PERFETTO_DFATAL_OR_ELOG(
483           "Received invalid data source instance to start: %" PRIu64, id);
484     }
485     return;
486   }
487 
488   DataSource& data_source = it->second;
489   if (data_source.started) {
490     PERFETTO_DFATAL_OR_ELOG(
491         "Trying to start already started data-source: %" PRIu64, id);
492     return;
493   }
494   const HeapprofdConfig& heapprofd_config = data_source.config;
495 
496   // Central daemon - set system properties for any targets that start later,
497   // and signal already-running targets to start the profiling client.
498   if (mode_ == HeapprofdMode::kCentral) {
499     if (!heapprofd_config.no_startup())
500       SetStartupProperties(&data_source);
501     if (!heapprofd_config.no_running())
502       SignalRunningProcesses(&data_source);
503   }
504 
505   const auto continuous_dump_config = heapprofd_config.continuous_dump_config();
506   uint32_t dump_interval = continuous_dump_config.dump_interval_ms();
507   if (dump_interval) {
508     auto weak_producer = weak_factory_.GetWeakPtr();
509     task_runner_->PostDelayedTask(
510         [weak_producer, id, dump_interval] {
511           if (!weak_producer)
512             return;
513           weak_producer->DoContinuousDump(id, dump_interval);
514         },
515         continuous_dump_config.dump_phase_ms());
516   }
517   data_source.started = true;
518   PERFETTO_DLOG("Started DataSource");
519 }
520 
UnwinderForPID(pid_t pid)521 UnwindingWorker& HeapprofdProducer::UnwinderForPID(pid_t pid) {
522   return unwinding_workers_[static_cast<uint64_t>(pid) % kUnwinderThreads];
523 }
524 
StopDataSource(DataSourceInstanceID id)525 void HeapprofdProducer::StopDataSource(DataSourceInstanceID id) {
526   auto it = data_sources_.find(id);
527   if (it == data_sources_.end()) {
528     endpoint_->NotifyDataSourceStopped(id);
529     if (mode_ == HeapprofdMode::kCentral)
530       PERFETTO_DFATAL_OR_ELOG(
531           "Trying to stop non existing data source: %" PRIu64, id);
532     return;
533   }
534 
535   DataSource& data_source = it->second;
536   data_source.was_stopped = true;
537   ShutdownDataSource(&data_source);
538 }
539 
ShutdownDataSource(DataSource * data_source)540 void HeapprofdProducer::ShutdownDataSource(DataSource* data_source) {
541   data_source->shutting_down = true;
542   // If no processes connected, or all of them have already disconnected
543   // (and have been dumped) and no PIDs have been rejected,
544   // MaybeFinishDataSource can tear down the data source.
545   if (MaybeFinishDataSource(data_source))
546     return;
547 
548   if (!data_source->rejected_pids.empty()) {
549     auto trace_packet = data_source->trace_writer->NewTracePacket();
550     ProfilePacket* profile_packet = trace_packet->set_profile_packet();
551     for (pid_t rejected_pid : data_source->rejected_pids) {
552       ProfilePacket::ProcessHeapSamples* proto =
553           profile_packet->add_process_dumps();
554       proto->set_pid(static_cast<uint64_t>(rejected_pid));
555       proto->set_rejected_concurrent(true);
556     }
557     trace_packet->Finalize();
558     data_source->rejected_pids.clear();
559     if (MaybeFinishDataSource(data_source))
560       return;
561   }
562 
563   for (const auto& pid_and_process_state : data_source->process_states) {
564     pid_t pid = pid_and_process_state.first;
565     UnwinderForPID(pid).PostDisconnectSocket(pid);
566   }
567 
568   auto id = data_source->id;
569   auto weak_producer = weak_factory_.GetWeakPtr();
570   task_runner_->PostDelayedTask(
571       [weak_producer, id] {
572         if (!weak_producer)
573           return;
574         auto ds_it = weak_producer->data_sources_.find(id);
575         if (ds_it != weak_producer->data_sources_.end()) {
576           PERFETTO_ELOG("Final dump timed out.");
577           DataSource& ds = ds_it->second;
578           // Do not dump any stragglers, just trigger the Flush and tear down
579           // the data source.
580           ds.process_states.clear();
581           ds.rejected_pids.clear();
582           PERFETTO_CHECK(weak_producer->MaybeFinishDataSource(&ds));
583         }
584       },
585       data_source->stop_timeout_ms);
586 }
587 
DoContinuousDump(DataSourceInstanceID id,uint32_t dump_interval)588 void HeapprofdProducer::DoContinuousDump(DataSourceInstanceID id,
589                                          uint32_t dump_interval) {
590   if (!DumpProcessesInDataSource(id))
591     return;
592   auto weak_producer = weak_factory_.GetWeakPtr();
593   task_runner_->PostDelayedTask(
594       [weak_producer, id, dump_interval] {
595         if (!weak_producer)
596           return;
597         weak_producer->DoContinuousDump(id, dump_interval);
598       },
599       dump_interval);
600 }
601 
DumpProcessState(DataSource * data_source,pid_t pid,ProcessState * process_state)602 void HeapprofdProducer::DumpProcessState(DataSource* data_source,
603                                          pid_t pid,
604                                          ProcessState* process_state) {
605   HeapTracker& heap_tracker = process_state->heap_tracker;
606 
607   bool from_startup =
608       data_source->signaled_pids.find(pid) == data_source->signaled_pids.cend();
609   uint64_t dump_timestamp;
610   if (data_source->config.dump_at_max())
611     dump_timestamp = heap_tracker.max_timestamp();
612   else
613     dump_timestamp = heap_tracker.committed_timestamp();
614   auto new_heapsamples = [pid, from_startup, dump_timestamp, process_state,
615                           data_source](
616                              ProfilePacket::ProcessHeapSamples* proto) {
617     proto->set_pid(static_cast<uint64_t>(pid));
618     proto->set_timestamp(dump_timestamp);
619     proto->set_from_startup(from_startup);
620     proto->set_disconnected(process_state->disconnected);
621     proto->set_buffer_overran(process_state->buffer_overran);
622     proto->set_buffer_corrupted(process_state->buffer_corrupted);
623     proto->set_hit_guardrail(data_source->hit_guardrail);
624     auto* stats = proto->set_stats();
625     stats->set_unwinding_errors(process_state->unwinding_errors);
626     stats->set_heap_samples(process_state->heap_samples);
627     stats->set_map_reparses(process_state->map_reparses);
628     stats->set_total_unwinding_time_us(process_state->total_unwinding_time_us);
629     auto* unwinding_hist = stats->set_unwinding_time_us();
630     for (const auto& p : process_state->unwinding_time_us.GetData()) {
631       auto* bucket = unwinding_hist->add_buckets();
632       if (p.first == LogHistogram::kMaxBucket)
633         bucket->set_max_bucket(true);
634       else
635         bucket->set_upper_limit(p.first);
636       bucket->set_count(p.second);
637     }
638   };
639 
640   DumpState dump_state(data_source->trace_writer.get(),
641                        std::move(new_heapsamples), &data_source->intern_state);
642 
643   if (process_state->page_idle_checker) {
644     PageIdleChecker& page_idle_checker = *process_state->page_idle_checker;
645     heap_tracker.GetAllocations([&dump_state, &page_idle_checker](
646                                     uint64_t addr, uint64_t,
647                                     uint64_t alloc_size,
648                                     uint64_t callstack_id) {
649       int64_t idle =
650           page_idle_checker.OnIdlePage(addr, static_cast<size_t>(alloc_size));
651       if (idle < 0) {
652         PERFETTO_PLOG("OnIdlePage.");
653         return;
654       }
655       if (idle > 0)
656         dump_state.AddIdleBytes(callstack_id, static_cast<uint64_t>(idle));
657     });
658   }
659 
660   heap_tracker.GetCallstackAllocations(
661       [&dump_state,
662        &data_source](const HeapTracker::CallstackAllocations& alloc) {
663         dump_state.WriteAllocation(alloc, data_source->config.dump_at_max());
664       });
665   if (process_state->page_idle_checker)
666     process_state->page_idle_checker->MarkPagesIdle();
667   dump_state.DumpCallstacks(&callsites_);
668 }
669 
DumpProcessesInDataSource(DataSourceInstanceID id)670 bool HeapprofdProducer::DumpProcessesInDataSource(DataSourceInstanceID id) {
671   auto it = data_sources_.find(id);
672   if (it == data_sources_.end()) {
673     PERFETTO_LOG(
674         "Data source not found (harmless if using continuous_dump_config).");
675     return false;
676   }
677   DataSource& data_source = it->second;
678 
679   for (std::pair<const pid_t, ProcessState>& pid_and_process_state :
680        data_source.process_states) {
681     pid_t pid = pid_and_process_state.first;
682     ProcessState& process_state = pid_and_process_state.second;
683     DumpProcessState(&data_source, pid, &process_state);
684   }
685 
686   return true;
687 }
688 
DumpAll()689 void HeapprofdProducer::DumpAll() {
690   for (const auto& id_and_data_source : data_sources_) {
691     if (!DumpProcessesInDataSource(id_and_data_source.first))
692       PERFETTO_DLOG("Failed to dump %" PRIu64, id_and_data_source.first);
693   }
694 }
695 
Flush(FlushRequestID flush_id,const DataSourceInstanceID * ids,size_t num_ids)696 void HeapprofdProducer::Flush(FlushRequestID flush_id,
697                               const DataSourceInstanceID* ids,
698                               size_t num_ids) {
699   size_t& flush_in_progress = flushes_in_progress_[flush_id];
700   PERFETTO_DCHECK(flush_in_progress == 0);
701   flush_in_progress = num_ids;
702   for (size_t i = 0; i < num_ids; ++i) {
703     auto it = data_sources_.find(ids[i]);
704     if (it == data_sources_.end()) {
705       PERFETTO_DFATAL_OR_ELOG("Trying to flush unknown data-source %" PRIu64,
706                               ids[i]);
707       flush_in_progress--;
708       continue;
709     }
710     DataSource& data_source = it->second;
711     auto weak_producer = weak_factory_.GetWeakPtr();
712 
713     auto callback = [weak_producer, flush_id] {
714       if (weak_producer)
715         // Reposting because this task runner could be on a different thread
716         // than the IPC task runner.
717         return weak_producer->task_runner_->PostTask([weak_producer, flush_id] {
718           if (weak_producer)
719             return weak_producer->FinishDataSourceFlush(flush_id);
720         });
721     };
722     data_source.trace_writer->Flush(std::move(callback));
723   }
724   if (flush_in_progress == 0) {
725     endpoint_->NotifyFlushComplete(flush_id);
726     flushes_in_progress_.erase(flush_id);
727   }
728 }
729 
FinishDataSourceFlush(FlushRequestID flush_id)730 void HeapprofdProducer::FinishDataSourceFlush(FlushRequestID flush_id) {
731   auto it = flushes_in_progress_.find(flush_id);
732   if (it == flushes_in_progress_.end()) {
733     PERFETTO_DFATAL_OR_ELOG("FinishDataSourceFlush id invalid: %" PRIu64,
734                             flush_id);
735     return;
736   }
737   size_t& flush_in_progress = it->second;
738   if (--flush_in_progress == 0) {
739     endpoint_->NotifyFlushComplete(flush_id);
740     flushes_in_progress_.erase(flush_id);
741   }
742 }
743 
OnDisconnect(base::UnixSocket * self)744 void HeapprofdProducer::SocketDelegate::OnDisconnect(base::UnixSocket* self) {
745   auto it = producer_->pending_processes_.find(self->peer_pid());
746   if (it == producer_->pending_processes_.end()) {
747     PERFETTO_DFATAL_OR_ELOG("Unexpected disconnect.");
748     return;
749   }
750 
751   if (self == it->second.sock.get())
752     producer_->pending_processes_.erase(it);
753 }
754 
OnNewIncomingConnection(base::UnixSocket *,std::unique_ptr<base::UnixSocket> new_connection)755 void HeapprofdProducer::SocketDelegate::OnNewIncomingConnection(
756     base::UnixSocket*,
757     std::unique_ptr<base::UnixSocket> new_connection) {
758   Process peer_process;
759   peer_process.pid = new_connection->peer_pid();
760   if (!GetCmdlineForPID(peer_process.pid, &peer_process.cmdline))
761     PERFETTO_PLOG("Failed to get cmdline for %d", peer_process.pid);
762 
763   producer_->HandleClientConnection(std::move(new_connection), peer_process);
764 }
765 
OnDataAvailable(base::UnixSocket * self)766 void HeapprofdProducer::SocketDelegate::OnDataAvailable(
767     base::UnixSocket* self) {
768   auto it = producer_->pending_processes_.find(self->peer_pid());
769   if (it == producer_->pending_processes_.end()) {
770     PERFETTO_DFATAL_OR_ELOG("Unexpected data.");
771     return;
772   }
773 
774   PendingProcess& pending_process = it->second;
775 
776   base::ScopedFile fds[kHandshakeSize];
777   char buf[1];
778   self->Receive(buf, sizeof(buf), fds, base::ArraySize(fds));
779 
780   static_assert(kHandshakeSize == 3, "change if and else if below.");
781   // We deliberately do not check for fds[kHandshakePageIdle] so we can
782   // degrade gracefully on kernels that do not have the file yet.
783   if (fds[kHandshakeMaps] && fds[kHandshakeMem]) {
784     auto ds_it =
785         producer_->data_sources_.find(pending_process.data_source_instance_id);
786     if (ds_it == producer_->data_sources_.end()) {
787       producer_->pending_processes_.erase(it);
788       return;
789     }
790     DataSource& data_source = ds_it->second;
791 
792     if (data_source.shutting_down) {
793       producer_->pending_processes_.erase(it);
794       PERFETTO_LOG("Got handshake for DS that is shutting down. Rejecting.");
795       return;
796     }
797 
798     auto it_and_inserted = data_source.process_states.emplace(
799         std::piecewise_construct, std::forward_as_tuple(self->peer_pid()),
800         std::forward_as_tuple(&producer_->callsites_,
801                               data_source.config.dump_at_max()));
802 
803     ProcessState& process_state = it_and_inserted.first->second;
804     if (data_source.config.idle_allocations()) {
805       if (fds[kHandshakePageIdle]) {
806         process_state.page_idle_checker =
807             PageIdleChecker(std::move(fds[kHandshakePageIdle]));
808       } else {
809         PERFETTO_ELOG(
810             "Idle page tracking requested but did not receive "
811             "page_idle file. Continuing without idle page tracking. Please "
812             "check your kernel version.");
813       }
814     }
815 
816     PERFETTO_DLOG("%d: Received FDs.", self->peer_pid());
817     int raw_fd = pending_process.shmem.fd();
818     // TODO(fmayer): Full buffer could deadlock us here.
819     if (!self->Send(&data_source.client_configuration,
820                     sizeof(data_source.client_configuration), &raw_fd, 1)) {
821       // If Send fails, the socket will have been Shutdown, and the raw socket
822       // closed.
823       producer_->pending_processes_.erase(it);
824       return;
825     }
826 
827     UnwindingWorker::HandoffData handoff_data;
828     handoff_data.data_source_instance_id =
829         pending_process.data_source_instance_id;
830     handoff_data.sock = self->ReleaseSocket();
831     handoff_data.maps_fd = std::move(fds[kHandshakeMaps]);
832     handoff_data.mem_fd = std::move(fds[kHandshakeMem]);
833     handoff_data.shmem = std::move(pending_process.shmem);
834     handoff_data.client_config = data_source.client_configuration;
835 
836     producer_->UnwinderForPID(self->peer_pid())
837         .PostHandoffSocket(std::move(handoff_data));
838     producer_->pending_processes_.erase(it);
839   } else if (fds[kHandshakeMaps] || fds[kHandshakeMem] ||
840              fds[kHandshakePageIdle]) {
841     PERFETTO_DFATAL_OR_ELOG("%d: Received partial FDs.", self->peer_pid());
842     producer_->pending_processes_.erase(it);
843   } else {
844     PERFETTO_ELOG("%d: Received no FDs.", self->peer_pid());
845   }
846 }
847 
GetDataSourceForProcess(const Process & proc)848 HeapprofdProducer::DataSource* HeapprofdProducer::GetDataSourceForProcess(
849     const Process& proc) {
850   for (auto& ds_id_and_datasource : data_sources_) {
851     DataSource& ds = ds_id_and_datasource.second;
852     if (ConfigTargetsProcess(ds.config, proc, ds.normalized_cmdlines))
853       return &ds;
854   }
855   return nullptr;
856 }
857 
RecordOtherSourcesAsRejected(DataSource * active_ds,const Process & proc)858 void HeapprofdProducer::RecordOtherSourcesAsRejected(DataSource* active_ds,
859                                                      const Process& proc) {
860   for (auto& ds_id_and_datasource : data_sources_) {
861     DataSource& ds = ds_id_and_datasource.second;
862     if (&ds != active_ds &&
863         ConfigTargetsProcess(ds.config, proc, ds.normalized_cmdlines))
864       ds.rejected_pids.emplace(proc.pid);
865   }
866 }
867 
HandleClientConnection(std::unique_ptr<base::UnixSocket> new_connection,Process process)868 void HeapprofdProducer::HandleClientConnection(
869     std::unique_ptr<base::UnixSocket> new_connection,
870     Process process) {
871   DataSource* data_source = GetDataSourceForProcess(process);
872   if (!data_source) {
873     PERFETTO_LOG("No data source found.");
874     return;
875   }
876   RecordOtherSourcesAsRejected(data_source, process);
877 
878   uint64_t shmem_size = data_source->config.shmem_size_bytes();
879   if (!shmem_size)
880     shmem_size = kDefaultShmemSize;
881   if (shmem_size > kMaxShmemSize)
882     shmem_size = kMaxShmemSize;
883 
884   auto shmem = SharedRingBuffer::Create(static_cast<size_t>(shmem_size));
885   if (!shmem || !shmem->is_valid()) {
886     PERFETTO_LOG("Failed to create shared memory.");
887     return;
888   }
889 
890   pid_t peer_pid = new_connection->peer_pid();
891   if (peer_pid != process.pid) {
892     PERFETTO_DFATAL_OR_ELOG("Invalid PID connected.");
893     return;
894   }
895 
896   PendingProcess pending_process;
897   pending_process.sock = std::move(new_connection);
898   pending_process.data_source_instance_id = data_source->id;
899   pending_process.shmem = std::move(*shmem);
900   pending_processes_.emplace(peer_pid, std::move(pending_process));
901 }
902 
PostAllocRecord(AllocRecord alloc_rec)903 void HeapprofdProducer::PostAllocRecord(AllocRecord alloc_rec) {
904   // Once we can use C++14, this should be std::moved into the lambda instead.
905   AllocRecord* raw_alloc_rec = new AllocRecord(std::move(alloc_rec));
906   auto weak_this = weak_factory_.GetWeakPtr();
907   task_runner_->PostTask([weak_this, raw_alloc_rec] {
908     if (weak_this)
909       weak_this->HandleAllocRecord(std::move(*raw_alloc_rec));
910     delete raw_alloc_rec;
911   });
912 }
913 
PostFreeRecord(FreeRecord free_rec)914 void HeapprofdProducer::PostFreeRecord(FreeRecord free_rec) {
915   // Once we can use C++14, this should be std::moved into the lambda instead.
916   FreeRecord* raw_free_rec = new FreeRecord(std::move(free_rec));
917   auto weak_this = weak_factory_.GetWeakPtr();
918   task_runner_->PostTask([weak_this, raw_free_rec] {
919     if (weak_this)
920       weak_this->HandleFreeRecord(std::move(*raw_free_rec));
921     delete raw_free_rec;
922   });
923 }
924 
PostSocketDisconnected(DataSourceInstanceID ds_id,pid_t pid,SharedRingBuffer::Stats stats)925 void HeapprofdProducer::PostSocketDisconnected(DataSourceInstanceID ds_id,
926                                                pid_t pid,
927                                                SharedRingBuffer::Stats stats) {
928   auto weak_this = weak_factory_.GetWeakPtr();
929   task_runner_->PostTask([weak_this, ds_id, pid, stats] {
930     if (weak_this)
931       weak_this->HandleSocketDisconnected(ds_id, pid, stats);
932   });
933 }
934 
HandleAllocRecord(AllocRecord alloc_rec)935 void HeapprofdProducer::HandleAllocRecord(AllocRecord alloc_rec) {
936   const AllocMetadata& alloc_metadata = alloc_rec.alloc_metadata;
937   auto it = data_sources_.find(alloc_rec.data_source_instance_id);
938   if (it == data_sources_.end()) {
939     PERFETTO_LOG("Invalid data source in alloc record.");
940     return;
941   }
942 
943   DataSource& ds = it->second;
944   auto process_state_it = ds.process_states.find(alloc_rec.pid);
945   if (process_state_it == ds.process_states.end()) {
946     PERFETTO_LOG("Invalid PID in alloc record.");
947     return;
948   }
949 
950   const auto& prefixes = ds.config.skip_symbol_prefix();
951   if (!prefixes.empty()) {
952     for (FrameData& frame_data : alloc_rec.frames) {
953       const std::string& map = frame_data.frame.map_name;
954       if (std::find_if(prefixes.cbegin(), prefixes.cend(),
955                        [&map](const std::string& prefix) {
956                          return base::StartsWith(map, prefix);
957                        }) != prefixes.cend()) {
958         frame_data.frame.function_name = "FILTERED";
959       }
960     }
961   }
962 
963   ProcessState& process_state = process_state_it->second;
964   HeapTracker& heap_tracker = process_state.heap_tracker;
965 
966   if (alloc_rec.error)
967     process_state.unwinding_errors++;
968   if (alloc_rec.reparsed_map)
969     process_state.map_reparses++;
970   process_state.heap_samples++;
971   process_state.unwinding_time_us.Add(alloc_rec.unwinding_time_us);
972   process_state.total_unwinding_time_us += alloc_rec.unwinding_time_us;
973 
974   // abspc may no longer refer to the same functions, as we had to reparse
975   // maps. Reset the cache.
976   if (alloc_rec.reparsed_map)
977     heap_tracker.ClearFrameCache();
978 
979   heap_tracker.RecordMalloc(alloc_rec.frames, alloc_metadata.alloc_address,
980                             alloc_metadata.sample_size,
981                             alloc_metadata.alloc_size,
982                             alloc_metadata.sequence_number,
983                             alloc_metadata.clock_monotonic_coarse_timestamp);
984 }
985 
HandleFreeRecord(FreeRecord free_rec)986 void HeapprofdProducer::HandleFreeRecord(FreeRecord free_rec) {
987   const FreeBatch& free_batch = free_rec.free_batch;
988   auto it = data_sources_.find(free_rec.data_source_instance_id);
989   if (it == data_sources_.end()) {
990     PERFETTO_LOG("Invalid data source in free record.");
991     return;
992   }
993 
994   DataSource& ds = it->second;
995   auto process_state_it = ds.process_states.find(free_rec.pid);
996   if (process_state_it == ds.process_states.end()) {
997     PERFETTO_LOG("Invalid PID in free record.");
998     return;
999   }
1000 
1001   ProcessState& process_state = process_state_it->second;
1002   HeapTracker& heap_tracker = process_state.heap_tracker;
1003 
1004   const FreeBatchEntry* entries = free_batch.entries;
1005   uint64_t num_entries = free_batch.num_entries;
1006   if (num_entries > kFreeBatchSize) {
1007     PERFETTO_DFATAL_OR_ELOG("Malformed free page.");
1008     return;
1009   }
1010   for (size_t i = 0; i < num_entries; ++i) {
1011     const FreeBatchEntry& entry = entries[i];
1012     heap_tracker.RecordFree(entry.addr, entry.sequence_number,
1013                             free_batch.clock_monotonic_coarse_timestamp);
1014   }
1015 }
1016 
MaybeFinishDataSource(DataSource * ds)1017 bool HeapprofdProducer::MaybeFinishDataSource(DataSource* ds) {
1018   if (!ds->process_states.empty() || !ds->rejected_pids.empty() ||
1019       !ds->shutting_down) {
1020     return false;
1021   }
1022 
1023   bool was_stopped = ds->was_stopped;
1024   DataSourceInstanceID ds_id = ds->id;
1025   auto weak_producer = weak_factory_.GetWeakPtr();
1026   ds->trace_writer->Flush([weak_producer, ds_id, was_stopped] {
1027     if (!weak_producer)
1028       return;
1029 
1030     if (was_stopped)
1031       weak_producer->endpoint_->NotifyDataSourceStopped(ds_id);
1032     weak_producer->data_sources_.erase(ds_id);
1033 
1034     if (weak_producer->mode_ == HeapprofdMode::kChild) {
1035       // Post this as a task to allow NotifyDataSourceStopped to post tasks.
1036       weak_producer->task_runner_->PostTask([weak_producer] {
1037         if (!weak_producer)
1038           return;
1039         weak_producer->TerminateProcess(
1040             /*exit_status=*/0);  // does not return
1041       });
1042     }
1043   });
1044   return true;
1045 }
1046 
HandleSocketDisconnected(DataSourceInstanceID ds_id,pid_t pid,SharedRingBuffer::Stats stats)1047 void HeapprofdProducer::HandleSocketDisconnected(
1048     DataSourceInstanceID ds_id,
1049     pid_t pid,
1050     SharedRingBuffer::Stats stats) {
1051   auto it = data_sources_.find(ds_id);
1052   if (it == data_sources_.end())
1053     return;
1054   DataSource& ds = it->second;
1055 
1056   auto process_state_it = ds.process_states.find(pid);
1057   if (process_state_it == ds.process_states.end())
1058     return;
1059   ProcessState& process_state = process_state_it->second;
1060   process_state.disconnected = !ds.shutting_down;
1061   process_state.buffer_overran =
1062       stats.num_writes_overflow > 0 && !ds.config.block_client();
1063   process_state.buffer_corrupted =
1064       stats.num_writes_corrupt > 0 || stats.num_reads_corrupt > 0;
1065 
1066   DumpProcessState(&ds, pid, &process_state);
1067   ds.process_states.erase(pid);
1068   MaybeFinishDataSource(&ds);
1069 }
1070 
CheckDataSourceCpu()1071 void HeapprofdProducer::CheckDataSourceCpu() {
1072   auto weak_producer = weak_factory_.GetWeakPtr();
1073   task_runner_->PostDelayedTask(
1074       [weak_producer] {
1075         if (!weak_producer)
1076           return;
1077         weak_producer->CheckDataSourceCpu();
1078       },
1079       kGuardrailIntervalMs);
1080 
1081   bool any_guardrail = false;
1082   for (auto& id_and_ds : data_sources_) {
1083     DataSource& ds = id_and_ds.second;
1084     if (ds.config.max_heapprofd_cpu_secs() > 0)
1085       any_guardrail = true;
1086   }
1087 
1088   if (!any_guardrail)
1089     return;
1090 
1091   base::Optional<uint64_t> cputime_sec = GetCputimeSec();
1092   if (!cputime_sec) {
1093     PERFETTO_ELOG("Failed to get CPU time.");
1094     return;
1095   }
1096 
1097   for (auto& id_and_ds : data_sources_) {
1098     DataSource& ds = id_and_ds.second;
1099     uint64_t ds_max_cpu = ds.config.max_heapprofd_cpu_secs();
1100     if (ds_max_cpu > 0) {
1101       // We reject data-sources with CPU guardrails if we cannot read the
1102       // initial value.
1103       PERFETTO_CHECK(ds.start_cputime_sec);
1104       uint64_t cpu_diff = *cputime_sec - *ds.start_cputime_sec;
1105       if (*cputime_sec > *ds.start_cputime_sec && cpu_diff > ds_max_cpu) {
1106         PERFETTO_ELOG(
1107             "Exceeded data-source CPU guardrail "
1108             "(%" PRIu64 " > %" PRIu64 "). Shutting down.",
1109             cpu_diff, ds_max_cpu);
1110         ds.hit_guardrail = true;
1111         ShutdownDataSource(&ds);
1112       }
1113     }
1114   }
1115 }
1116 
CheckDataSourceMemory()1117 void HeapprofdProducer::CheckDataSourceMemory() {
1118   auto weak_producer = weak_factory_.GetWeakPtr();
1119   task_runner_->PostDelayedTask(
1120       [weak_producer] {
1121         if (!weak_producer)
1122           return;
1123         weak_producer->CheckDataSourceMemory();
1124       },
1125       kGuardrailIntervalMs);
1126 
1127   bool any_guardrail = false;
1128   for (auto& id_and_ds : data_sources_) {
1129     DataSource& ds = id_and_ds.second;
1130     if (ds.config.max_heapprofd_memory_kb() > 0)
1131       any_guardrail = true;
1132   }
1133 
1134   if (!any_guardrail)
1135     return;
1136 
1137   base::Optional<uint32_t> anon_and_swap;
1138   base::Optional<std::string> status = ReadStatus(getpid());
1139   if (status)
1140     anon_and_swap = GetRssAnonAndSwap(*status);
1141 
1142   if (!anon_and_swap) {
1143     PERFETTO_ELOG("Failed to read heapprofd memory.");
1144     return;
1145   }
1146 
1147   for (auto& id_and_ds : data_sources_) {
1148     DataSource& ds = id_and_ds.second;
1149     uint32_t ds_max_mem = ds.config.max_heapprofd_memory_kb();
1150     if (ds_max_mem > 0 && *anon_and_swap > ds_max_mem) {
1151       PERFETTO_ELOG("Exceeded data-source memory guardrail (%" PRIu32
1152                     " > %" PRIu32 "). Shutting down.",
1153                     *anon_and_swap, ds_max_mem);
1154       ds.hit_guardrail = true;
1155       ShutdownDataSource(&ds);
1156     }
1157   }
1158 }
1159 
1160 }  // namespace profiling
1161 }  // namespace perfetto
1162