• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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/tracing/ipc/producer/producer_ipc_client_impl.h"
18 
19 #include <cinttypes>
20 
21 #include <string.h>
22 
23 #include "perfetto/base/logging.h"
24 #include "perfetto/base/task_runner.h"
25 #include "perfetto/ext/base/version.h"
26 #include "perfetto/ext/ipc/client.h"
27 #include "perfetto/ext/tracing/core/commit_data_request.h"
28 #include "perfetto/ext/tracing/core/producer.h"
29 #include "perfetto/ext/tracing/core/shared_memory_arbiter.h"
30 #include "perfetto/ext/tracing/core/trace_writer.h"
31 #include "perfetto/tracing/core/data_source_config.h"
32 #include "perfetto/tracing/core/data_source_descriptor.h"
33 #include "perfetto/tracing/core/trace_config.h"
34 
35 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
36 #include "src/tracing/ipc/shared_memory_windows.h"
37 #else
38 #include "src/tracing/ipc/posix_shared_memory.h"
39 #endif
40 
41 // TODO(fmayer): think to what happens when ProducerIPCClientImpl gets destroyed
42 // w.r.t. the Producer pointer. Also think to lifetime of the Producer* during
43 // the callbacks.
44 
45 namespace perfetto {
46 
47 // static. (Declared in include/tracing/ipc/producer_ipc_client.h).
Connect(const char * service_sock_name,Producer * producer,const std::string & producer_name,base::TaskRunner * task_runner,TracingService::ProducerSMBScrapingMode smb_scraping_mode,size_t shared_memory_size_hint_bytes,size_t shared_memory_page_size_hint_bytes,std::unique_ptr<SharedMemory> shm,std::unique_ptr<SharedMemoryArbiter> shm_arbiter,ConnectionFlags conn_flags)48 std::unique_ptr<TracingService::ProducerEndpoint> ProducerIPCClient::Connect(
49     const char* service_sock_name,
50     Producer* producer,
51     const std::string& producer_name,
52     base::TaskRunner* task_runner,
53     TracingService::ProducerSMBScrapingMode smb_scraping_mode,
54     size_t shared_memory_size_hint_bytes,
55     size_t shared_memory_page_size_hint_bytes,
56     std::unique_ptr<SharedMemory> shm,
57     std::unique_ptr<SharedMemoryArbiter> shm_arbiter,
58     ConnectionFlags conn_flags) {
59   return std::unique_ptr<TracingService::ProducerEndpoint>(
60       new ProducerIPCClientImpl(
61           {service_sock_name,
62            conn_flags ==
63                ProducerIPCClient::ConnectionFlags::kRetryIfUnreachable},
64           producer, producer_name, task_runner, smb_scraping_mode,
65           shared_memory_size_hint_bytes, shared_memory_page_size_hint_bytes,
66           std::move(shm), std::move(shm_arbiter)));
67 }
68 
69 // static. (Declared in include/tracing/ipc/producer_ipc_client.h).
Connect(ipc::Client::ConnArgs conn_args,Producer * producer,const std::string & producer_name,base::TaskRunner * task_runner,TracingService::ProducerSMBScrapingMode smb_scraping_mode,size_t shared_memory_size_hint_bytes,size_t shared_memory_page_size_hint_bytes,std::unique_ptr<SharedMemory> shm,std::unique_ptr<SharedMemoryArbiter> shm_arbiter)70 std::unique_ptr<TracingService::ProducerEndpoint> ProducerIPCClient::Connect(
71     ipc::Client::ConnArgs conn_args,
72     Producer* producer,
73     const std::string& producer_name,
74     base::TaskRunner* task_runner,
75     TracingService::ProducerSMBScrapingMode smb_scraping_mode,
76     size_t shared_memory_size_hint_bytes,
77     size_t shared_memory_page_size_hint_bytes,
78     std::unique_ptr<SharedMemory> shm,
79     std::unique_ptr<SharedMemoryArbiter> shm_arbiter) {
80   return std::unique_ptr<TracingService::ProducerEndpoint>(
81       new ProducerIPCClientImpl(std::move(conn_args), producer, producer_name,
82                                 task_runner, smb_scraping_mode,
83                                 shared_memory_size_hint_bytes,
84                                 shared_memory_page_size_hint_bytes,
85                                 std::move(shm), std::move(shm_arbiter)));
86 }
87 
ProducerIPCClientImpl(ipc::Client::ConnArgs conn_args,Producer * producer,const std::string & producer_name,base::TaskRunner * task_runner,TracingService::ProducerSMBScrapingMode smb_scraping_mode,size_t shared_memory_size_hint_bytes,size_t shared_memory_page_size_hint_bytes,std::unique_ptr<SharedMemory> shm,std::unique_ptr<SharedMemoryArbiter> shm_arbiter)88 ProducerIPCClientImpl::ProducerIPCClientImpl(
89     ipc::Client::ConnArgs conn_args,
90     Producer* producer,
91     const std::string& producer_name,
92     base::TaskRunner* task_runner,
93     TracingService::ProducerSMBScrapingMode smb_scraping_mode,
94     size_t shared_memory_size_hint_bytes,
95     size_t shared_memory_page_size_hint_bytes,
96     std::unique_ptr<SharedMemory> shm,
97     std::unique_ptr<SharedMemoryArbiter> shm_arbiter)
98     : producer_(producer),
99       task_runner_(task_runner),
100       ipc_channel_(
101           ipc::Client::CreateInstance(std::move(conn_args), task_runner)),
102       producer_port_(this /* event_listener */),
103       shared_memory_(std::move(shm)),
104       shared_memory_arbiter_(std::move(shm_arbiter)),
105       name_(producer_name),
106       shared_memory_page_size_hint_bytes_(shared_memory_page_size_hint_bytes),
107       shared_memory_size_hint_bytes_(shared_memory_size_hint_bytes),
108       smb_scraping_mode_(smb_scraping_mode) {
109   // Check for producer-provided SMB (used by Chrome for startup tracing).
110   if (shared_memory_) {
111     // We also expect a valid (unbound) arbiter. Bind it to this endpoint now.
112     PERFETTO_CHECK(shared_memory_arbiter_);
113     shared_memory_arbiter_->BindToProducerEndpoint(this, task_runner_);
114 
115     // If the service accepts our SMB, then it must match our requested page
116     // layout. The protocol doesn't allow the service to change the size and
117     // layout when the SMB is provided by the producer.
118     shared_buffer_page_size_kb_ = shared_memory_page_size_hint_bytes_ / 1024;
119   }
120 
121   ipc_channel_->BindService(producer_port_.GetWeakPtr());
122   PERFETTO_DCHECK_THREAD(thread_checker_);
123 }
124 
~ProducerIPCClientImpl()125 ProducerIPCClientImpl::~ProducerIPCClientImpl() {
126   PERFETTO_DCHECK_THREAD(thread_checker_);
127 }
128 
129 // Called by the IPC layer if the BindService() succeeds.
OnConnect()130 void ProducerIPCClientImpl::OnConnect() {
131   PERFETTO_DCHECK_THREAD(thread_checker_);
132   connected_ = true;
133 
134   // The IPC layer guarantees that any outstanding callback will be dropped on
135   // the floor if producer_port_ is destroyed between the request and the reply.
136   // Binding |this| is hence safe.
137   ipc::Deferred<protos::gen::InitializeConnectionResponse> on_init;
138   on_init.Bind(
139       [this](ipc::AsyncResult<protos::gen::InitializeConnectionResponse> resp) {
140         OnConnectionInitialized(
141             resp.success(),
142             resp.success() ? resp->using_shmem_provided_by_producer() : false,
143             resp.success() ? resp->direct_smb_patching_supported() : false);
144       });
145   protos::gen::InitializeConnectionRequest req;
146   req.set_producer_name(name_);
147   req.set_shared_memory_size_hint_bytes(
148       static_cast<uint32_t>(shared_memory_size_hint_bytes_));
149   req.set_shared_memory_page_size_hint_bytes(
150       static_cast<uint32_t>(shared_memory_page_size_hint_bytes_));
151   switch (smb_scraping_mode_) {
152     case TracingService::ProducerSMBScrapingMode::kDefault:
153       // No need to set the mode, it defaults to use the service default if
154       // unspecified.
155       break;
156     case TracingService::ProducerSMBScrapingMode::kEnabled:
157       req.set_smb_scraping_mode(
158           protos::gen::InitializeConnectionRequest::SMB_SCRAPING_ENABLED);
159       break;
160     case TracingService::ProducerSMBScrapingMode::kDisabled:
161       req.set_smb_scraping_mode(
162           protos::gen::InitializeConnectionRequest::SMB_SCRAPING_DISABLED);
163       break;
164   }
165 
166   int shm_fd = -1;
167   if (shared_memory_) {
168     req.set_producer_provided_shmem(true);
169 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
170     auto key = static_cast<SharedMemoryWindows*>(shared_memory_.get())->key();
171     req.set_shm_key_windows(key);
172 #else
173     shm_fd = static_cast<PosixSharedMemory*>(shared_memory_.get())->fd();
174 #endif
175   }
176 
177   req.set_sdk_version(base::GetVersionString());
178   producer_port_.InitializeConnection(req, std::move(on_init), shm_fd);
179 
180   // Create the back channel to receive commands from the Service.
181   ipc::Deferred<protos::gen::GetAsyncCommandResponse> on_cmd;
182   on_cmd.Bind(
183       [this](ipc::AsyncResult<protos::gen::GetAsyncCommandResponse> resp) {
184         if (!resp)
185           return;  // The IPC channel was closed and |resp| was auto-rejected.
186         OnServiceRequest(*resp);
187       });
188   producer_port_.GetAsyncCommand(protos::gen::GetAsyncCommandRequest(),
189                                  std::move(on_cmd));
190 
191   // If there are pending Sync() requests, send them now.
192   for (const auto& pending_sync : pending_sync_reqs_)
193     Sync(std::move(pending_sync));
194   pending_sync_reqs_.clear();
195 }
196 
OnDisconnect()197 void ProducerIPCClientImpl::OnDisconnect() {
198   PERFETTO_DCHECK_THREAD(thread_checker_);
199   PERFETTO_DLOG("Tracing service connection failure");
200   connected_ = false;
201   data_sources_setup_.clear();
202   producer_->OnDisconnect();  // Note: may delete |this|.
203 }
204 
OnConnectionInitialized(bool connection_succeeded,bool using_shmem_provided_by_producer,bool direct_smb_patching_supported)205 void ProducerIPCClientImpl::OnConnectionInitialized(
206     bool connection_succeeded,
207     bool using_shmem_provided_by_producer,
208     bool direct_smb_patching_supported) {
209   PERFETTO_DCHECK_THREAD(thread_checker_);
210   // If connection_succeeded == false, the OnDisconnect() call will follow next
211   // and there we'll notify the |producer_|. TODO: add a test for this.
212   if (!connection_succeeded)
213     return;
214   is_shmem_provided_by_producer_ = using_shmem_provided_by_producer;
215   direct_smb_patching_supported_ = direct_smb_patching_supported;
216   producer_->OnConnect();
217 
218   // Bail out if the service failed to adopt our producer-allocated SMB.
219   // TODO(eseckler): Handle adoption failure more gracefully.
220   if (shared_memory_ && !is_shmem_provided_by_producer_) {
221     PERFETTO_DLOG("Service failed adopt producer-provided SMB, disconnecting.");
222     ipc_channel_.reset();
223     return;
224   }
225 }
226 
OnServiceRequest(const protos::gen::GetAsyncCommandResponse & cmd)227 void ProducerIPCClientImpl::OnServiceRequest(
228     const protos::gen::GetAsyncCommandResponse& cmd) {
229   PERFETTO_DCHECK_THREAD(thread_checker_);
230 
231   // This message is sent only when connecting to a service running Android Q+.
232   // See comment below in kStartDataSource.
233   if (cmd.has_setup_data_source()) {
234     const auto& req = cmd.setup_data_source();
235     const DataSourceInstanceID dsid = req.new_instance_id();
236     data_sources_setup_.insert(dsid);
237     producer_->SetupDataSource(dsid, req.config());
238     return;
239   }
240 
241   if (cmd.has_start_data_source()) {
242     const auto& req = cmd.start_data_source();
243     const DataSourceInstanceID dsid = req.new_instance_id();
244     const DataSourceConfig& cfg = req.config();
245     if (!data_sources_setup_.count(dsid)) {
246       // When connecting with an older (Android P) service, the service will not
247       // send a SetupDataSource message. We synthesize it here in that case.
248       producer_->SetupDataSource(dsid, cfg);
249     }
250     producer_->StartDataSource(dsid, cfg);
251     return;
252   }
253 
254   if (cmd.has_stop_data_source()) {
255     const DataSourceInstanceID dsid = cmd.stop_data_source().instance_id();
256     producer_->StopDataSource(dsid);
257     data_sources_setup_.erase(dsid);
258     return;
259   }
260 
261   if (cmd.has_setup_tracing()) {
262     std::unique_ptr<SharedMemory> ipc_shared_memory;
263 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
264     const std::string& shm_key = cmd.setup_tracing().shm_key_windows();
265     if (!shm_key.empty())
266       ipc_shared_memory = SharedMemoryWindows::Attach(shm_key);
267 #else
268     base::ScopedFile shmem_fd = ipc_channel_->TakeReceivedFD();
269     if (shmem_fd) {
270       // TODO(primiano): handle mmap failure in case of OOM.
271       ipc_shared_memory =
272           PosixSharedMemory::AttachToFd(std::move(shmem_fd),
273                                         /*require_seals_if_supported=*/false);
274     }
275 #endif
276     if (ipc_shared_memory) {
277       // This is the nominal case used in most configurations, where the service
278       // provides the SMB.
279       PERFETTO_CHECK(!is_shmem_provided_by_producer_ && !shared_memory_);
280       shared_memory_ = std::move(ipc_shared_memory);
281       shared_buffer_page_size_kb_ =
282           cmd.setup_tracing().shared_buffer_page_size_kb();
283       shared_memory_arbiter_ = SharedMemoryArbiter::CreateInstance(
284           shared_memory_.get(), shared_buffer_page_size_kb_ * 1024, this,
285           task_runner_);
286       if (direct_smb_patching_supported_)
287         shared_memory_arbiter_->SetDirectSMBPatchingSupportedByService();
288     } else {
289       // Producer-provided SMB (used by Chrome for startup tracing).
290       PERFETTO_CHECK(is_shmem_provided_by_producer_ && shared_memory_ &&
291                      shared_memory_arbiter_);
292     }
293     producer_->OnTracingSetup();
294     return;
295   }
296 
297   if (cmd.has_flush()) {
298     // This cast boilerplate is required only because protobuf uses its own
299     // uint64 and not stdint's uint64_t. On some 64 bit archs they differ on the
300     // type (long vs long long) even though they have the same size.
301     const auto* data_source_ids = cmd.flush().data_source_ids().data();
302     static_assert(sizeof(data_source_ids[0]) == sizeof(DataSourceInstanceID),
303                   "data_source_ids should be 64-bit");
304     producer_->Flush(
305         cmd.flush().request_id(),
306         reinterpret_cast<const DataSourceInstanceID*>(data_source_ids),
307         static_cast<size_t>(cmd.flush().data_source_ids().size()));
308     return;
309   }
310 
311   if (cmd.has_clear_incremental_state()) {
312     const auto* data_source_ids =
313         cmd.clear_incremental_state().data_source_ids().data();
314     static_assert(sizeof(data_source_ids[0]) == sizeof(DataSourceInstanceID),
315                   "data_source_ids should be 64-bit");
316     producer_->ClearIncrementalState(
317         reinterpret_cast<const DataSourceInstanceID*>(data_source_ids),
318         static_cast<size_t>(
319             cmd.clear_incremental_state().data_source_ids().size()));
320     return;
321   }
322 
323   PERFETTO_DFATAL("Unknown async request received from tracing service");
324 }
325 
RegisterDataSource(const DataSourceDescriptor & descriptor)326 void ProducerIPCClientImpl::RegisterDataSource(
327     const DataSourceDescriptor& descriptor) {
328   PERFETTO_DCHECK_THREAD(thread_checker_);
329   if (!connected_) {
330     PERFETTO_DLOG(
331         "Cannot RegisterDataSource(), not connected to tracing service");
332   }
333   protos::gen::RegisterDataSourceRequest req;
334   *req.mutable_data_source_descriptor() = descriptor;
335   ipc::Deferred<protos::gen::RegisterDataSourceResponse> async_response;
336   async_response.Bind(
337       [](ipc::AsyncResult<protos::gen::RegisterDataSourceResponse> response) {
338         if (!response)
339           PERFETTO_DLOG("RegisterDataSource() failed: connection reset");
340       });
341   producer_port_.RegisterDataSource(req, std::move(async_response));
342 }
343 
UpdateDataSource(const DataSourceDescriptor & descriptor)344 void ProducerIPCClientImpl::UpdateDataSource(
345     const DataSourceDescriptor& descriptor) {
346   PERFETTO_DCHECK_THREAD(thread_checker_);
347   if (!connected_) {
348     PERFETTO_DLOG(
349         "Cannot UpdateDataSource(), not connected to tracing service");
350   }
351   protos::gen::UpdateDataSourceRequest req;
352   *req.mutable_data_source_descriptor() = descriptor;
353   ipc::Deferred<protos::gen::UpdateDataSourceResponse> async_response;
354   async_response.Bind(
355       [](ipc::AsyncResult<protos::gen::UpdateDataSourceResponse> response) {
356         if (!response)
357           PERFETTO_DLOG("UpdateDataSource() failed: connection reset");
358       });
359   producer_port_.UpdateDataSource(req, std::move(async_response));
360 }
361 
UnregisterDataSource(const std::string & name)362 void ProducerIPCClientImpl::UnregisterDataSource(const std::string& name) {
363   PERFETTO_DCHECK_THREAD(thread_checker_);
364   if (!connected_) {
365     PERFETTO_DLOG(
366         "Cannot UnregisterDataSource(), not connected to tracing service");
367     return;
368   }
369   protos::gen::UnregisterDataSourceRequest req;
370   req.set_data_source_name(name);
371   producer_port_.UnregisterDataSource(
372       req, ipc::Deferred<protos::gen::UnregisterDataSourceResponse>());
373 }
374 
RegisterTraceWriter(uint32_t writer_id,uint32_t target_buffer)375 void ProducerIPCClientImpl::RegisterTraceWriter(uint32_t writer_id,
376                                                 uint32_t target_buffer) {
377   PERFETTO_DCHECK_THREAD(thread_checker_);
378   if (!connected_) {
379     PERFETTO_DLOG(
380         "Cannot RegisterTraceWriter(), not connected to tracing service");
381     return;
382   }
383   protos::gen::RegisterTraceWriterRequest req;
384   req.set_trace_writer_id(writer_id);
385   req.set_target_buffer(target_buffer);
386   producer_port_.RegisterTraceWriter(
387       req, ipc::Deferred<protos::gen::RegisterTraceWriterResponse>());
388 }
389 
UnregisterTraceWriter(uint32_t writer_id)390 void ProducerIPCClientImpl::UnregisterTraceWriter(uint32_t writer_id) {
391   PERFETTO_DCHECK_THREAD(thread_checker_);
392   if (!connected_) {
393     PERFETTO_DLOG(
394         "Cannot UnregisterTraceWriter(), not connected to tracing service");
395     return;
396   }
397   protos::gen::UnregisterTraceWriterRequest req;
398   req.set_trace_writer_id(writer_id);
399   producer_port_.UnregisterTraceWriter(
400       req, ipc::Deferred<protos::gen::UnregisterTraceWriterResponse>());
401 }
402 
CommitData(const CommitDataRequest & req,CommitDataCallback callback)403 void ProducerIPCClientImpl::CommitData(const CommitDataRequest& req,
404                                        CommitDataCallback callback) {
405   PERFETTO_DCHECK_THREAD(thread_checker_);
406   if (!connected_) {
407     PERFETTO_DLOG("Cannot CommitData(), not connected to tracing service");
408     return;
409   }
410   ipc::Deferred<protos::gen::CommitDataResponse> async_response;
411   // TODO(primiano): add a test that destroys ProducerIPCClientImpl soon after
412   // this call and checks that the callback is dropped.
413   if (callback) {
414     async_response.Bind(
415         [callback](ipc::AsyncResult<protos::gen::CommitDataResponse> response) {
416           if (!response) {
417             PERFETTO_DLOG("CommitData() failed: connection reset");
418             return;
419           }
420           callback();
421         });
422   }
423   producer_port_.CommitData(req, std::move(async_response));
424 }
425 
NotifyDataSourceStarted(DataSourceInstanceID id)426 void ProducerIPCClientImpl::NotifyDataSourceStarted(DataSourceInstanceID id) {
427   PERFETTO_DCHECK_THREAD(thread_checker_);
428   if (!connected_) {
429     PERFETTO_DLOG(
430         "Cannot NotifyDataSourceStarted(), not connected to tracing service");
431     return;
432   }
433   protos::gen::NotifyDataSourceStartedRequest req;
434   req.set_data_source_id(id);
435   producer_port_.NotifyDataSourceStarted(
436       req, ipc::Deferred<protos::gen::NotifyDataSourceStartedResponse>());
437 }
438 
NotifyDataSourceStopped(DataSourceInstanceID id)439 void ProducerIPCClientImpl::NotifyDataSourceStopped(DataSourceInstanceID id) {
440   PERFETTO_DCHECK_THREAD(thread_checker_);
441   if (!connected_) {
442     PERFETTO_DLOG(
443         "Cannot NotifyDataSourceStopped(), not connected to tracing service");
444     return;
445   }
446   protos::gen::NotifyDataSourceStoppedRequest req;
447   req.set_data_source_id(id);
448   producer_port_.NotifyDataSourceStopped(
449       req, ipc::Deferred<protos::gen::NotifyDataSourceStoppedResponse>());
450 }
451 
ActivateTriggers(const std::vector<std::string> & triggers)452 void ProducerIPCClientImpl::ActivateTriggers(
453     const std::vector<std::string>& triggers) {
454   PERFETTO_DCHECK_THREAD(thread_checker_);
455   if (!connected_) {
456     PERFETTO_DLOG(
457         "Cannot ActivateTriggers(), not connected to tracing service");
458     return;
459   }
460   protos::gen::ActivateTriggersRequest proto_req;
461   for (const auto& name : triggers) {
462     *proto_req.add_trigger_names() = name;
463   }
464   producer_port_.ActivateTriggers(
465       proto_req, ipc::Deferred<protos::gen::ActivateTriggersResponse>());
466 }
467 
Sync(std::function<void ()> callback)468 void ProducerIPCClientImpl::Sync(std::function<void()> callback) {
469   PERFETTO_DCHECK_THREAD(thread_checker_);
470   if (!connected_) {
471     pending_sync_reqs_.emplace_back(std::move(callback));
472     return;
473   }
474   ipc::Deferred<protos::gen::SyncResponse> resp;
475   resp.Bind([callback](ipc::AsyncResult<protos::gen::SyncResponse>) {
476     // Here we ACK the callback even if the service replies with a failure
477     // (i.e. the service is too old and doesn't understand Sync()). In that
478     // case the service has still seen the request, the IPC roundtrip is
479     // still a (weaker) linearization fence.
480     callback();
481   });
482   producer_port_.Sync(protos::gen::SyncRequest(), std::move(resp));
483 }
484 
CreateTraceWriter(BufferID target_buffer,BufferExhaustedPolicy buffer_exhausted_policy)485 std::unique_ptr<TraceWriter> ProducerIPCClientImpl::CreateTraceWriter(
486     BufferID target_buffer,
487     BufferExhaustedPolicy buffer_exhausted_policy) {
488   // This method can be called by different threads. |shared_memory_arbiter_| is
489   // thread-safe but be aware of accessing any other state in this function.
490   return shared_memory_arbiter_->CreateTraceWriter(target_buffer,
491                                                    buffer_exhausted_policy);
492 }
493 
MaybeSharedMemoryArbiter()494 SharedMemoryArbiter* ProducerIPCClientImpl::MaybeSharedMemoryArbiter() {
495   return shared_memory_arbiter_.get();
496 }
497 
IsShmemProvidedByProducer() const498 bool ProducerIPCClientImpl::IsShmemProvidedByProducer() const {
499   return is_shmem_provided_by_producer_;
500 }
501 
NotifyFlushComplete(FlushRequestID req_id)502 void ProducerIPCClientImpl::NotifyFlushComplete(FlushRequestID req_id) {
503   return shared_memory_arbiter_->NotifyFlushComplete(req_id);
504 }
505 
shared_memory() const506 SharedMemory* ProducerIPCClientImpl::shared_memory() const {
507   return shared_memory_.get();
508 }
509 
shared_buffer_page_size_kb() const510 size_t ProducerIPCClientImpl::shared_buffer_page_size_kb() const {
511   return shared_buffer_page_size_kb_;
512 }
513 
514 }  // namespace perfetto
515