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