• 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/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(Service * core_service)36 ProducerIPCService::ProducerIPCService(Service* 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   // ConnectProducer will call OnConnect() on the next task.
69   producer->service_endpoint = core_service_->ConnectProducer(
70       producer.get(), client_info.uid(), req.producer_name(),
71       req.shared_memory_size_hint_bytes());
72 
73   // Could happen if the service has too many producers connected.
74   if (!producer->service_endpoint)
75     response.Reject();
76 
77   producers_.emplace(ipc_client_id, std::move(producer));
78   // Because of the std::move() |producer| is invalid after this point.
79 
80   auto async_res =
81       ipc::AsyncResult<protos::InitializeConnectionResponse>::Create();
82   response.Resolve(std::move(async_res));
83 }
84 
85 // Called by the remote Producer through the IPC channel.
RegisterDataSource(const protos::RegisterDataSourceRequest & req,DeferredRegisterDataSourceResponse response)86 void ProducerIPCService::RegisterDataSource(
87     const protos::RegisterDataSourceRequest& req,
88     DeferredRegisterDataSourceResponse response) {
89   RemoteProducer* producer = GetProducerForCurrentRequest();
90   if (!producer) {
91     PERFETTO_DLOG(
92         "Producer invoked RegisterDataSource() before InitializeConnection()");
93     return response.Reject();
94   }
95 
96   DataSourceDescriptor dsd;
97   dsd.FromProto(req.data_source_descriptor());
98   GetProducerForCurrentRequest()->service_endpoint->RegisterDataSource(dsd);
99 
100   // RegisterDataSource doesn't expect any meaningful response.
101   response.Resolve(
102       ipc::AsyncResult<protos::RegisterDataSourceResponse>::Create());
103 }
104 
105 // Called by the IPC layer.
OnClientDisconnected()106 void ProducerIPCService::OnClientDisconnected() {
107   ipc::ClientID client_id = ipc::Service::client_info().client_id();
108   PERFETTO_DLOG("Client %" PRIu64 " disconnected", client_id);
109   producers_.erase(client_id);
110 }
111 
112 // TODO(fmayer): test what happens if we receive the following tasks, in order:
113 // RegisterDataSource, UnregisterDataSource, OnDataSourceRegistered.
114 // which essentially means that the client posted back to back a
115 // ReqisterDataSource and UnregisterDataSource speculating on the next id.
116 // Called by the remote Service through the IPC channel.
UnregisterDataSource(const protos::UnregisterDataSourceRequest & req,DeferredUnregisterDataSourceResponse response)117 void ProducerIPCService::UnregisterDataSource(
118     const protos::UnregisterDataSourceRequest& req,
119     DeferredUnregisterDataSourceResponse response) {
120   RemoteProducer* producer = GetProducerForCurrentRequest();
121   if (!producer) {
122     PERFETTO_DLOG(
123         "Producer invoked UnregisterDataSource() before "
124         "InitializeConnection()");
125     return response.Reject();
126   }
127   producer->service_endpoint->UnregisterDataSource(req.data_source_name());
128 
129   // UnregisterDataSource doesn't expect any meaningful response.
130   response.Resolve(
131       ipc::AsyncResult<protos::UnregisterDataSourceResponse>::Create());
132 }
133 
CommitData(const protos::CommitDataRequest & proto_req,DeferredCommitDataResponse resp)134 void ProducerIPCService::CommitData(const protos::CommitDataRequest& proto_req,
135                                     DeferredCommitDataResponse resp) {
136   RemoteProducer* producer = GetProducerForCurrentRequest();
137   if (!producer) {
138     PERFETTO_DLOG(
139         "Producer invoked CommitData() before InitializeConnection()");
140     return;
141   }
142   CommitDataRequest req;
143   req.FromProto(proto_req);
144 
145   // We don't want to send a response if the client didn't attach a callback to
146   // the original request. Doing so would generate unnecessary wakeups and
147   // context switches.
148   std::function<void()> callback;
149   if (resp.IsBound()) {
150     // Capturing |resp| by reference here speculates on the fact that
151     // CommitData() in service_impl.cc invokes the passed callback inline,
152     // without posting it. If that assumption changes this code needs to wrap
153     // the response in a shared_ptr (C+11 lambdas don't support move) and use
154     // a weak ptr in the caller.
155     callback = [&resp] {
156       resp.Resolve(ipc::AsyncResult<protos::CommitDataResponse>::Create());
157     };
158   }
159   producer->service_endpoint->CommitData(req, callback);
160 }
161 
GetAsyncCommand(const protos::GetAsyncCommandRequest &,DeferredGetAsyncCommandResponse response)162 void ProducerIPCService::GetAsyncCommand(
163     const protos::GetAsyncCommandRequest&,
164     DeferredGetAsyncCommandResponse response) {
165   RemoteProducer* producer = GetProducerForCurrentRequest();
166   if (!producer) {
167     PERFETTO_DLOG(
168         "Producer invoked GetAsyncCommand() before "
169         "InitializeConnection()");
170     return response.Reject();
171   }
172   // Keep the back channel open, without ever resolving the ipc::Deferred fully,
173   // to send async commands to the RemoteProducer (e.g., starting/stopping a
174   // data source).
175   producer->async_producer_commands = std::move(response);
176 }
177 
178 ////////////////////////////////////////////////////////////////////////////////
179 // RemoteProducer methods
180 ////////////////////////////////////////////////////////////////////////////////
181 
182 ProducerIPCService::RemoteProducer::RemoteProducer() = default;
183 ProducerIPCService::RemoteProducer::~RemoteProducer() = default;
184 
185 // Invoked by the |core_service_| business logic after the ConnectProducer()
186 // call. There is nothing to do here, we really expected the ConnectProducer()
187 // to just work in the local case.
OnConnect()188 void ProducerIPCService::RemoteProducer::OnConnect() {}
189 
190 // Invoked by the |core_service_| business logic after we destroy the
191 // |service_endpoint| (in the RemoteProducer dtor).
OnDisconnect()192 void ProducerIPCService::RemoteProducer::OnDisconnect() {}
193 
194 // Invoked by the |core_service_| business logic when it wants to start a new
195 // data source.
CreateDataSourceInstance(DataSourceInstanceID dsid,const DataSourceConfig & cfg)196 void ProducerIPCService::RemoteProducer::CreateDataSourceInstance(
197     DataSourceInstanceID dsid,
198     const DataSourceConfig& cfg) {
199   if (!async_producer_commands.IsBound()) {
200     PERFETTO_DLOG(
201         "The Service tried to start a new data source but the remote Producer "
202         "has not yet initialized the connection");
203     return;
204   }
205   auto cmd = ipc::AsyncResult<protos::GetAsyncCommandResponse>::Create();
206   cmd.set_has_more(true);
207   cmd->mutable_start_data_source()->set_new_instance_id(dsid);
208   cfg.ToProto(cmd->mutable_start_data_source()->mutable_config());
209   async_producer_commands.Resolve(std::move(cmd));
210 }
211 
TearDownDataSourceInstance(DataSourceInstanceID dsid)212 void ProducerIPCService::RemoteProducer::TearDownDataSourceInstance(
213     DataSourceInstanceID dsid) {
214   if (!async_producer_commands.IsBound()) {
215     PERFETTO_DLOG(
216         "The Service tried to stop a data source but the remote Producer "
217         "has not yet initialized the connection");
218     return;
219   }
220   auto cmd = ipc::AsyncResult<protos::GetAsyncCommandResponse>::Create();
221   cmd.set_has_more(true);
222   cmd->mutable_stop_data_source()->set_instance_id(dsid);
223   async_producer_commands.Resolve(std::move(cmd));
224 }
225 
OnTracingSetup()226 void ProducerIPCService::RemoteProducer::OnTracingSetup() {
227   if (!async_producer_commands.IsBound()) {
228     PERFETTO_DLOG(
229         "The Service tried to allocate the shared memory but the remote "
230         "Producer has not yet initialized the connection");
231     return;
232   }
233   PERFETTO_CHECK(service_endpoint->shared_memory());
234   const int shm_fd =
235       static_cast<PosixSharedMemory*>(service_endpoint->shared_memory())->fd();
236   auto cmd = ipc::AsyncResult<protos::GetAsyncCommandResponse>::Create();
237   cmd.set_has_more(true);
238   cmd.set_fd(shm_fd);
239   cmd->mutable_setup_tracing()->set_shared_buffer_page_size_kb(
240       static_cast<uint32_t>(service_endpoint->shared_buffer_page_size_kb()));
241   async_producer_commands.Resolve(std::move(cmd));
242 }
243 
Flush(FlushRequestID flush_request_id,const DataSourceInstanceID * data_source_ids,size_t num_data_sources)244 void ProducerIPCService::RemoteProducer::Flush(
245     FlushRequestID flush_request_id,
246     const DataSourceInstanceID* data_source_ids,
247     size_t num_data_sources) {
248   if (!async_producer_commands.IsBound()) {
249     PERFETTO_DLOG(
250         "The Service tried to request a flush but the remote Producer has not "
251         "yet initialized the connection");
252     return;
253   }
254   auto cmd = ipc::AsyncResult<protos::GetAsyncCommandResponse>::Create();
255   cmd.set_has_more(true);
256   for (size_t i = 0; i < num_data_sources; i++)
257     cmd->mutable_flush()->add_data_source_ids(data_source_ids[i]);
258   cmd->mutable_flush()->set_request_id(flush_request_id);
259   async_producer_commands.Resolve(std::move(cmd));
260 }
261 
262 }  // namespace perfetto
263