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