• 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/service/producer_ipc_service.h"
18 
19 #include <inttypes.h>
20 
21 #include "perfetto/base/logging.h"
22 #include "perfetto/base/task_runner.h"
23 #include "perfetto/ipc/host.h"
24 #include "perfetto/tracing/core/commit_data_request.h"
25 #include "perfetto/tracing/core/data_source_config.h"
26 #include "perfetto/tracing/core/data_source_descriptor.h"
27 #include "perfetto/tracing/core/tracing_service.h"
28 #include "src/tracing/ipc/posix_shared_memory.h"
29 
30 // The remote Producer(s) are not trusted. All the methods from the ProducerPort
31 // IPC layer (e.g. RegisterDataSource()) must assume that the remote Producer is
32 // compromised.
33 
34 namespace perfetto {
35 
ProducerIPCService(TracingService * core_service)36 ProducerIPCService::ProducerIPCService(TracingService* core_service)
37     : core_service_(core_service), weak_ptr_factory_(this) {}
38 
39 ProducerIPCService::~ProducerIPCService() = default;
40 
41 ProducerIPCService::RemoteProducer*
GetProducerForCurrentRequest()42 ProducerIPCService::GetProducerForCurrentRequest() {
43   const ipc::ClientID ipc_client_id = ipc::Service::client_info().client_id();
44   PERFETTO_CHECK(ipc_client_id);
45   auto it = producers_.find(ipc_client_id);
46   if (it == producers_.end())
47     return nullptr;
48   return it->second.get();
49 }
50 
51 // Called by the remote Producer through the IPC channel soon after connecting.
InitializeConnection(const protos::InitializeConnectionRequest & req,DeferredInitializeConnectionResponse response)52 void ProducerIPCService::InitializeConnection(
53     const protos::InitializeConnectionRequest& req,
54     DeferredInitializeConnectionResponse response) {
55   const auto& client_info = ipc::Service::client_info();
56   const ipc::ClientID ipc_client_id = client_info.client_id();
57   PERFETTO_CHECK(ipc_client_id);
58 
59   if (producers_.count(ipc_client_id) > 0) {
60     PERFETTO_DLOG(
61         "The remote Producer is trying to re-initialize the connection");
62     return response.Reject();
63   }
64 
65   // Create a new entry.
66   std::unique_ptr<RemoteProducer> producer(new RemoteProducer());
67 
68   TracingService::ProducerSMBScrapingMode smb_scraping_mode =
69       TracingService::ProducerSMBScrapingMode::kDefault;
70   switch (req.smb_scraping_mode()) {
71     case protos::InitializeConnectionRequest::SMB_SCRAPING_UNSPECIFIED:
72       break;
73     case protos::InitializeConnectionRequest::SMB_SCRAPING_DISABLED:
74       smb_scraping_mode = TracingService::ProducerSMBScrapingMode::kDisabled;
75       break;
76     case protos::InitializeConnectionRequest::SMB_SCRAPING_ENABLED:
77       smb_scraping_mode = TracingService::ProducerSMBScrapingMode::kEnabled;
78       break;
79   }
80 
81   // ConnectProducer will call OnConnect() on the next task.
82   producer->service_endpoint = core_service_->ConnectProducer(
83       producer.get(), client_info.uid(), req.producer_name(),
84       req.shared_memory_size_hint_bytes(), /*in_process=*/false,
85       smb_scraping_mode);
86 
87   // Could happen if the service has too many producers connected.
88   if (!producer->service_endpoint)
89     response.Reject();
90 
91   producers_.emplace(ipc_client_id, std::move(producer));
92   // Because of the std::move() |producer| is invalid after this point.
93 
94   auto async_res =
95       ipc::AsyncResult<protos::InitializeConnectionResponse>::Create();
96   response.Resolve(std::move(async_res));
97 }
98 
99 // Called by the remote Producer through the IPC channel.
RegisterDataSource(const protos::RegisterDataSourceRequest & req,DeferredRegisterDataSourceResponse response)100 void ProducerIPCService::RegisterDataSource(
101     const protos::RegisterDataSourceRequest& req,
102     DeferredRegisterDataSourceResponse response) {
103   RemoteProducer* producer = GetProducerForCurrentRequest();
104   if (!producer) {
105     PERFETTO_DLOG(
106         "Producer invoked RegisterDataSource() before InitializeConnection()");
107     if (response.IsBound())
108       response.Reject();
109     return;
110   }
111 
112   DataSourceDescriptor dsd;
113   dsd.FromProto(req.data_source_descriptor());
114   GetProducerForCurrentRequest()->service_endpoint->RegisterDataSource(dsd);
115 
116   // RegisterDataSource doesn't expect any meaningful response.
117   if (response.IsBound()) {
118     response.Resolve(
119         ipc::AsyncResult<protos::RegisterDataSourceResponse>::Create());
120   }
121 }
122 
123 // Called by the IPC layer.
OnClientDisconnected()124 void ProducerIPCService::OnClientDisconnected() {
125   ipc::ClientID client_id = ipc::Service::client_info().client_id();
126   PERFETTO_DLOG("Client %" PRIu64 " disconnected", client_id);
127   producers_.erase(client_id);
128 }
129 
130 // TODO(fmayer): test what happens if we receive the following tasks, in order:
131 // RegisterDataSource, UnregisterDataSource, OnDataSourceRegistered.
132 // which essentially means that the client posted back to back a
133 // ReqisterDataSource and UnregisterDataSource speculating on the next id.
134 // Called by the remote Service through the IPC channel.
UnregisterDataSource(const protos::UnregisterDataSourceRequest & req,DeferredUnregisterDataSourceResponse response)135 void ProducerIPCService::UnregisterDataSource(
136     const protos::UnregisterDataSourceRequest& req,
137     DeferredUnregisterDataSourceResponse response) {
138   RemoteProducer* producer = GetProducerForCurrentRequest();
139   if (!producer) {
140     PERFETTO_DLOG(
141         "Producer invoked UnregisterDataSource() before "
142         "InitializeConnection()");
143     if (response.IsBound())
144       response.Reject();
145     return;
146   }
147   producer->service_endpoint->UnregisterDataSource(req.data_source_name());
148 
149   // UnregisterDataSource doesn't expect any meaningful response.
150   if (response.IsBound()) {
151     response.Resolve(
152         ipc::AsyncResult<protos::UnregisterDataSourceResponse>::Create());
153   }
154 }
155 
RegisterTraceWriter(const protos::RegisterTraceWriterRequest & req,DeferredRegisterTraceWriterResponse response)156 void ProducerIPCService::RegisterTraceWriter(
157     const protos::RegisterTraceWriterRequest& req,
158     DeferredRegisterTraceWriterResponse response) {
159   RemoteProducer* producer = GetProducerForCurrentRequest();
160   if (!producer) {
161     PERFETTO_DLOG(
162         "Producer invoked RegisterTraceWriter() before "
163         "InitializeConnection()");
164     if (response.IsBound())
165       response.Reject();
166     return;
167   }
168   producer->service_endpoint->RegisterTraceWriter(req.trace_writer_id(),
169                                                   req.target_buffer());
170 
171   // RegisterTraceWriter doesn't expect any meaningful response.
172   if (response.IsBound()) {
173     response.Resolve(
174         ipc::AsyncResult<protos::RegisterTraceWriterResponse>::Create());
175   }
176 }
177 
UnregisterTraceWriter(const protos::UnregisterTraceWriterRequest & req,DeferredUnregisterTraceWriterResponse response)178 void ProducerIPCService::UnregisterTraceWriter(
179     const protos::UnregisterTraceWriterRequest& req,
180     DeferredUnregisterTraceWriterResponse response) {
181   RemoteProducer* producer = GetProducerForCurrentRequest();
182   if (!producer) {
183     PERFETTO_DLOG(
184         "Producer invoked UnregisterTraceWriter() before "
185         "InitializeConnection()");
186     if (response.IsBound())
187       response.Reject();
188     return;
189   }
190   producer->service_endpoint->UnregisterTraceWriter(req.trace_writer_id());
191 
192   // UnregisterTraceWriter doesn't expect any meaningful response.
193   if (response.IsBound()) {
194     response.Resolve(
195         ipc::AsyncResult<protos::UnregisterTraceWriterResponse>::Create());
196   }
197 }
198 
CommitData(const protos::CommitDataRequest & proto_req,DeferredCommitDataResponse resp)199 void ProducerIPCService::CommitData(const protos::CommitDataRequest& proto_req,
200                                     DeferredCommitDataResponse resp) {
201   RemoteProducer* producer = GetProducerForCurrentRequest();
202   if (!producer) {
203     PERFETTO_DLOG(
204         "Producer invoked CommitData() before InitializeConnection()");
205     if (resp.IsBound())
206       resp.Reject();
207     return;
208   }
209   CommitDataRequest req;
210   req.FromProto(proto_req);
211 
212   // We don't want to send a response if the client didn't attach a callback to
213   // the original request. Doing so would generate unnecessary wakeups and
214   // context switches.
215   std::function<void()> callback;
216   if (resp.IsBound()) {
217     // Capturing |resp| by reference here speculates on the fact that
218     // CommitData() in tracing_service_impl.cc invokes the passed callback
219     // inline, without posting it. If that assumption changes this code needs to
220     // wrap the response in a shared_ptr (C+11 lambdas don't support move) and
221     // use a weak ptr in the caller.
222     callback = [&resp] {
223       resp.Resolve(ipc::AsyncResult<protos::CommitDataResponse>::Create());
224     };
225   }
226   producer->service_endpoint->CommitData(req, callback);
227 }
228 
NotifyDataSourceStarted(const protos::NotifyDataSourceStartedRequest & request,DeferredNotifyDataSourceStartedResponse response)229 void ProducerIPCService::NotifyDataSourceStarted(
230     const protos::NotifyDataSourceStartedRequest& request,
231     DeferredNotifyDataSourceStartedResponse response) {
232   RemoteProducer* producer = GetProducerForCurrentRequest();
233   if (!producer) {
234     PERFETTO_DLOG(
235         "Producer invoked NotifyDataSourceStarted() before "
236         "InitializeConnection()");
237     if (response.IsBound())
238       response.Reject();
239     return;
240   }
241   producer->service_endpoint->NotifyDataSourceStarted(request.data_source_id());
242 
243   // NotifyDataSourceStopped shouldn't expect any meaningful response, avoid
244   // a useless IPC in that case.
245   if (response.IsBound()) {
246     response.Resolve(
247         ipc::AsyncResult<protos::NotifyDataSourceStartedResponse>::Create());
248   }
249 }
250 
NotifyDataSourceStopped(const protos::NotifyDataSourceStoppedRequest & request,DeferredNotifyDataSourceStoppedResponse response)251 void ProducerIPCService::NotifyDataSourceStopped(
252     const protos::NotifyDataSourceStoppedRequest& request,
253     DeferredNotifyDataSourceStoppedResponse response) {
254   RemoteProducer* producer = GetProducerForCurrentRequest();
255   if (!producer) {
256     PERFETTO_DLOG(
257         "Producer invoked NotifyDataSourceStopped() before "
258         "InitializeConnection()");
259     if (response.IsBound())
260       response.Reject();
261     return;
262   }
263   producer->service_endpoint->NotifyDataSourceStopped(request.data_source_id());
264 
265   // NotifyDataSourceStopped shouldn't expect any meaningful response, avoid
266   // a useless IPC in that case.
267   if (response.IsBound()) {
268     response.Resolve(
269         ipc::AsyncResult<protos::NotifyDataSourceStoppedResponse>::Create());
270   }
271 }
272 
ActivateTriggers(const protos::ActivateTriggersRequest & proto_req,DeferredActivateTriggersResponse resp)273 void ProducerIPCService::ActivateTriggers(
274     const protos::ActivateTriggersRequest& proto_req,
275     DeferredActivateTriggersResponse resp) {
276   RemoteProducer* producer = GetProducerForCurrentRequest();
277   if (!producer) {
278     PERFETTO_DLOG(
279         "Producer invoked ActivateTriggers() before InitializeConnection()");
280     if (resp.IsBound())
281       resp.Reject();
282     return;
283   }
284   std::vector<std::string> triggers;
285   for (const auto& name : proto_req.trigger_names()) {
286     triggers.push_back(name);
287   }
288   producer->service_endpoint->ActivateTriggers(triggers);
289   // ActivateTriggers shouldn't expect any meaningful response, avoid
290   // a useless IPC in that case.
291   if (resp.IsBound()) {
292     resp.Resolve(ipc::AsyncResult<protos::ActivateTriggersResponse>::Create());
293   }
294 }
295 
GetAsyncCommand(const protos::GetAsyncCommandRequest &,DeferredGetAsyncCommandResponse response)296 void ProducerIPCService::GetAsyncCommand(
297     const protos::GetAsyncCommandRequest&,
298     DeferredGetAsyncCommandResponse response) {
299   RemoteProducer* producer = GetProducerForCurrentRequest();
300   if (!producer) {
301     PERFETTO_DLOG(
302         "Producer invoked GetAsyncCommand() before "
303         "InitializeConnection()");
304     return response.Reject();
305   }
306   // Keep the back channel open, without ever resolving the ipc::Deferred fully,
307   // to send async commands to the RemoteProducer (e.g., starting/stopping a
308   // data source).
309   producer->async_producer_commands = std::move(response);
310 }
311 
312 ////////////////////////////////////////////////////////////////////////////////
313 // RemoteProducer methods
314 ////////////////////////////////////////////////////////////////////////////////
315 
316 ProducerIPCService::RemoteProducer::RemoteProducer() = default;
317 ProducerIPCService::RemoteProducer::~RemoteProducer() = default;
318 
319 // Invoked by the |core_service_| business logic after the ConnectProducer()
320 // call. There is nothing to do here, we really expected the ConnectProducer()
321 // to just work in the local case.
OnConnect()322 void ProducerIPCService::RemoteProducer::OnConnect() {}
323 
324 // Invoked by the |core_service_| business logic after we destroy the
325 // |service_endpoint| (in the RemoteProducer dtor).
OnDisconnect()326 void ProducerIPCService::RemoteProducer::OnDisconnect() {}
327 
328 // Invoked by the |core_service_| business logic when it wants to create a new
329 // data source.
SetupDataSource(DataSourceInstanceID dsid,const DataSourceConfig & cfg)330 void ProducerIPCService::RemoteProducer::SetupDataSource(
331     DataSourceInstanceID dsid,
332     const DataSourceConfig& cfg) {
333   if (!async_producer_commands.IsBound()) {
334     PERFETTO_DLOG(
335         "The Service tried to create a new data source but the remote Producer "
336         "has not yet initialized the connection");
337     return;
338   }
339   auto cmd = ipc::AsyncResult<protos::GetAsyncCommandResponse>::Create();
340   cmd.set_has_more(true);
341   cmd->mutable_setup_data_source()->set_new_instance_id(dsid);
342   cfg.ToProto(cmd->mutable_setup_data_source()->mutable_config());
343   async_producer_commands.Resolve(std::move(cmd));
344 }
345 
346 // Invoked by the |core_service_| business logic when it wants to start a new
347 // data source.
StartDataSource(DataSourceInstanceID dsid,const DataSourceConfig & cfg)348 void ProducerIPCService::RemoteProducer::StartDataSource(
349     DataSourceInstanceID dsid,
350     const DataSourceConfig& cfg) {
351   if (!async_producer_commands.IsBound()) {
352     PERFETTO_DLOG(
353         "The Service tried to start a new data source but the remote Producer "
354         "has not yet initialized the connection");
355     return;
356   }
357   auto cmd = ipc::AsyncResult<protos::GetAsyncCommandResponse>::Create();
358   cmd.set_has_more(true);
359   cmd->mutable_start_data_source()->set_new_instance_id(dsid);
360   cfg.ToProto(cmd->mutable_start_data_source()->mutable_config());
361   async_producer_commands.Resolve(std::move(cmd));
362 }
363 
StopDataSource(DataSourceInstanceID dsid)364 void ProducerIPCService::RemoteProducer::StopDataSource(
365     DataSourceInstanceID dsid) {
366   if (!async_producer_commands.IsBound()) {
367     PERFETTO_DLOG(
368         "The Service tried to stop a data source but the remote Producer "
369         "has not yet initialized the connection");
370     return;
371   }
372   auto cmd = ipc::AsyncResult<protos::GetAsyncCommandResponse>::Create();
373   cmd.set_has_more(true);
374   cmd->mutable_stop_data_source()->set_instance_id(dsid);
375   async_producer_commands.Resolve(std::move(cmd));
376 }
377 
OnTracingSetup()378 void ProducerIPCService::RemoteProducer::OnTracingSetup() {
379   if (!async_producer_commands.IsBound()) {
380     PERFETTO_DLOG(
381         "The Service tried to allocate the shared memory but the remote "
382         "Producer has not yet initialized the connection");
383     return;
384   }
385   PERFETTO_CHECK(service_endpoint->shared_memory());
386   const int shm_fd =
387       static_cast<PosixSharedMemory*>(service_endpoint->shared_memory())->fd();
388   auto cmd = ipc::AsyncResult<protos::GetAsyncCommandResponse>::Create();
389   cmd.set_has_more(true);
390   cmd.set_fd(shm_fd);
391   cmd->mutable_setup_tracing()->set_shared_buffer_page_size_kb(
392       static_cast<uint32_t>(service_endpoint->shared_buffer_page_size_kb()));
393   async_producer_commands.Resolve(std::move(cmd));
394 }
395 
Flush(FlushRequestID flush_request_id,const DataSourceInstanceID * data_source_ids,size_t num_data_sources)396 void ProducerIPCService::RemoteProducer::Flush(
397     FlushRequestID flush_request_id,
398     const DataSourceInstanceID* data_source_ids,
399     size_t num_data_sources) {
400   if (!async_producer_commands.IsBound()) {
401     PERFETTO_DLOG(
402         "The Service tried to request a flush but the remote Producer has not "
403         "yet initialized the connection");
404     return;
405   }
406   auto cmd = ipc::AsyncResult<protos::GetAsyncCommandResponse>::Create();
407   cmd.set_has_more(true);
408   for (size_t i = 0; i < num_data_sources; i++)
409     cmd->mutable_flush()->add_data_source_ids(data_source_ids[i]);
410   cmd->mutable_flush()->set_request_id(flush_request_id);
411   async_producer_commands.Resolve(std::move(cmd));
412 }
413 
ClearIncrementalState(const DataSourceInstanceID * data_source_ids,size_t num_data_sources)414 void ProducerIPCService::RemoteProducer::ClearIncrementalState(
415     const DataSourceInstanceID* data_source_ids,
416     size_t num_data_sources) {
417   if (!async_producer_commands.IsBound()) {
418     PERFETTO_DLOG(
419         "The Service tried to request an incremental state invalidation, but "
420         "the remote Producer has not yet initialized the connection");
421     return;
422   }
423   auto cmd = ipc::AsyncResult<protos::GetAsyncCommandResponse>::Create();
424   cmd.set_has_more(true);
425   for (size_t i = 0; i < num_data_sources; i++)
426     cmd->mutable_clear_incremental_state()->add_data_source_ids(
427         data_source_ids[i]);
428   async_producer_commands.Resolve(std::move(cmd));
429 }
430 
431 }  // namespace perfetto
432