blob: bd8e2edede1ee774ddad071141450a9a08edadff [file] [log] [blame]
Primiano Tuccia6166482017-11-20 13:05:45 +00001/*
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 "tracing/src/ipc/service/producer_ipc_service.h"
18
19#include <inttypes.h>
20
21#include "base/logging.h"
22#include "base/task_runner.h"
23#include "ipc/host.h"
24#include "tracing/core/data_source_config.h"
25#include "tracing/core/data_source_descriptor.h"
26#include "tracing/core/service.h"
Primiano Tuccif54cae42017-11-21 19:37:13 +000027#include "tracing/src/ipc/posix_shared_memory.h"
Primiano Tuccia6166482017-11-20 13:05:45 +000028
29// The remote Producer(s) are not trusted. All the methods from the ProducerPort
30// IPC layer (e.g. RegisterDataSource()) must assume that the remote Producer is
31// compromised.
32
33namespace perfetto {
34
35ProducerIPCService::ProducerIPCService(Service* core_service)
36 : core_service_(core_service), weak_ptr_factory_(this) {}
37
38ProducerIPCService::~ProducerIPCService() = default;
39
40ProducerIPCService::RemoteProducer*
41ProducerIPCService::GetProducerForCurrentRequest() {
42 const ipc::ClientID ipc_client_id = ipc::Service::client_info().client_id();
43 PERFETTO_CHECK(ipc_client_id);
44 auto it = producers_.find(ipc_client_id);
45 if (it == producers_.end())
46 return nullptr;
47 return it->second.get();
48}
49
50// Called by the remote Producer through the IPC channel soon after connecting.
51void ProducerIPCService::InitializeConnection(
Primiano Tuccif54cae42017-11-21 19:37:13 +000052 const InitializeConnectionRequest& req,
Primiano Tuccia6166482017-11-20 13:05:45 +000053 DeferredInitializeConnectionResponse response) {
54 const ipc::ClientID ipc_client_id = ipc::Service::client_info().client_id();
55 PERFETTO_CHECK(ipc_client_id);
56
57 if (producers_.count(ipc_client_id) > 0) {
58 PERFETTO_DLOG(
59 "The remote Producer is trying to re-initialize the connection");
60 return response.Reject();
61 }
62
63 // Create a new entry.
64 std::unique_ptr<RemoteProducer> producer(new RemoteProducer());
65
66 // ConnectProducer will call OnConnect() on the next task.
Primiano Tuccif54cae42017-11-21 19:37:13 +000067 producer->service_endpoint = core_service_->ConnectProducer(
68 producer.get(), req.shared_buffer_size_hint_bytes());
69 const int shm_fd = static_cast<PosixSharedMemory*>(
70 producer->service_endpoint->shared_memory())
71 ->fd();
Primiano Tuccia6166482017-11-20 13:05:45 +000072 producers_.emplace(ipc_client_id, std::move(producer));
Primiano Tuccif54cae42017-11-21 19:37:13 +000073 // Because of the std::move() |producer| is invalid after this point.
74
75 auto async_res = ipc::AsyncResult<InitializeConnectionResponse>::Create();
76 async_res.set_fd(shm_fd);
77 response.Resolve(std::move(async_res));
Primiano Tuccia6166482017-11-20 13:05:45 +000078}
79
80// Called by the remote Producer through the IPC channel.
81void ProducerIPCService::RegisterDataSource(
82 const RegisterDataSourceRequest& req,
83 DeferredRegisterDataSourceResponse response) {
84 RemoteProducer* producer = GetProducerForCurrentRequest();
85 if (!producer) {
86 PERFETTO_DLOG(
87 "Producer invoked RegisterDataSource() before InitializeConnection()");
88 return response.Reject();
89 }
90
91 const std::string data_source_name = req.data_source_descriptor().name();
92 if (producer->pending_data_sources.count(data_source_name)) {
93 PERFETTO_DLOG(
94 "A RegisterDataSource() request for \"%s\" is already pending",
95 data_source_name.c_str());
96 return response.Reject();
97 }
98
99 // Deserialize IPC proto -> core DataSourceDescriptor. Keep this in sync with
100 // changes to data_source_descriptor.proto.
101 DataSourceDescriptor dsd;
102 dsd.name = data_source_name;
103 producer->pending_data_sources[data_source_name] = std::move(response);
104 auto weak_this = weak_ptr_factory_.GetWeakPtr();
105
106 // TODO: add test to cover the case of IPC going away before the
107 // RegisterDataSource callback is received.
108 const ipc::ClientID ipc_client_id = ipc::Service::client_info().client_id();
109 GetProducerForCurrentRequest()->service_endpoint->RegisterDataSource(
110 dsd, [weak_this, ipc_client_id, data_source_name](DataSourceID id) {
111 if (!weak_this)
112 return;
113 weak_this->OnDataSourceRegistered(ipc_client_id, data_source_name, id);
114 });
115}
116
117// Called by the Service business logic.
118void ProducerIPCService::OnDataSourceRegistered(ipc::ClientID ipc_client_id,
119 std::string data_source_name,
120 DataSourceID id) {
121 auto producer_it = producers_.find(ipc_client_id);
122 if (producer_it == producers_.end())
123 return; // The producer died in the meantime.
124 RemoteProducer* producer = producer_it->second.get();
125
126 auto it = producer->pending_data_sources.find(data_source_name);
127 PERFETTO_CHECK(it != producer->pending_data_sources.end());
128
129 PERFETTO_DLOG("Data source %s registered, Client:%" PRIu64 " ID: %" PRIu64,
130 data_source_name.c_str(), ipc_client_id, id);
131
132 DeferredRegisterDataSourceResponse ipc_response = std::move(it->second);
133 producer->pending_data_sources.erase(it);
134 auto response = ipc::AsyncResult<RegisterDataSourceResponse>::Create();
135 response->set_data_source_id(id);
136 ipc_response.Resolve(std::move(response));
137}
138
139// Called by the IPC layer.
140void ProducerIPCService::OnClientDisconnected() {
141 ipc::ClientID client_id = ipc::Service::client_info().client_id();
142 PERFETTO_DLOG("Client %" PRIu64 " disconnected", client_id);
143 producers_.erase(client_id);
144}
145
146// TODO: test what happens if we receive the following tasks, in order:
147// RegisterDataSource, UnregisterDataSource, OnDataSourceRegistered.
148// which essentially means that the client posted back to back a
149// ReqisterDataSource and UnregisterDataSource speculating on the next id.
150// Called by the remote Service through the IPC channel.
151void ProducerIPCService::UnregisterDataSource(
152 const UnregisterDataSourceRequest& req,
153 DeferredUnregisterDataSourceResponse response) {
154 RemoteProducer* producer = GetProducerForCurrentRequest();
155 if (!producer) {
156 PERFETTO_DLOG(
157 "Producer invoked UnregisterDataSource() before "
158 "InitializeConnection()");
159 return response.Reject();
160 }
161 producer->service_endpoint->UnregisterDataSource(req.data_source_id());
162
163 // UnregisterDataSource doesn't expect any meaningful response.
164 response.Resolve(ipc::AsyncResult<UnregisterDataSourceResponse>::Create());
165}
166
167void ProducerIPCService::NotifySharedMemoryUpdate(
168 const NotifySharedMemoryUpdateRequest& req,
169 DeferredNotifySharedMemoryUpdateResponse response) {
170 RemoteProducer* producer = GetProducerForCurrentRequest();
171 if (!producer) {
172 PERFETTO_DLOG(
173 "Producer invoked NotifySharedMemoryUpdate() before "
174 "InitializeConnection()");
175 return response.Reject();
176 }
177 // TODO: check that the page indexes are consistent with the size of the
178 // shared memory region (once the SHM logic is there). Also add a test for it.
179 std::vector<uint32_t> changed_pages;
180 changed_pages.reserve(req.changed_pages_size());
181 for (const uint32_t& changed_page : req.changed_pages())
182 changed_pages.push_back(changed_page);
183 producer->service_endpoint->NotifySharedMemoryUpdate(changed_pages);
184 response.Resolve(
185 ipc::AsyncResult<NotifySharedMemoryUpdateResponse>::Create());
186}
187
188void ProducerIPCService::GetAsyncCommand(
189 const GetAsyncCommandRequest&,
190 DeferredGetAsyncCommandResponse response) {
191 RemoteProducer* producer = GetProducerForCurrentRequest();
192 if (!producer) {
193 PERFETTO_DLOG(
194 "Producer invoked GetAsyncCommand() before "
195 "InitializeConnection()");
196 return response.Reject();
197 }
198 // Keep the back channel open, without ever resolving the ipc::Deferred fully,
199 // to send async commands to the RemoteProducer (e.g., starting/stopping a
200 // data source).
201 producer->async_producer_commands = std::move(response);
202}
203
204////////////////////////////////////////////////////////////////////////////////
205// RemoteProducer methods
206////////////////////////////////////////////////////////////////////////////////
207
208ProducerIPCService::RemoteProducer::RemoteProducer() = default;
209ProducerIPCService::RemoteProducer::~RemoteProducer() = default;
210
211// Invoked by the |core_service_| business logic after the ConnectProducer()
212// call. There is nothing to do here, we really expected the ConnectProducer()
213// to just work in the local case.
214void ProducerIPCService::RemoteProducer::OnConnect() {}
215
216// Invoked by the |core_service_| business logic after we destroy the
217// |service_endpoint| (in the RemoteProducer dtor).
218void ProducerIPCService::RemoteProducer::OnDisconnect() {}
219
220// Invoked by the |core_service_| business logic when it wants to start a new
221// data source.
222void ProducerIPCService::RemoteProducer::CreateDataSourceInstance(
223 DataSourceInstanceID dsid,
224 const DataSourceConfig& cfg) {
225 if (!async_producer_commands.IsBound()) {
226 PERFETTO_DLOG(
227 "The Service tried to start a new data source but the remote Producer "
228 "has not yet initialized the connection");
229 return;
230 }
231 auto cmd = ipc::AsyncResult<GetAsyncCommandResponse>::Create();
232 cmd.set_has_more(true);
233 cmd->mutable_start_data_source()->set_new_instance_id(dsid);
234
235 // Keep this in sync with data_source_config.proto.
236 cmd->mutable_start_data_source()
237 ->mutable_config()
238 ->set_trace_category_filters(cfg.trace_category_filters);
239 async_producer_commands.Resolve(std::move(cmd));
240}
241
242void ProducerIPCService::RemoteProducer::TearDownDataSourceInstance(
243 DataSourceInstanceID dsid) {
244 if (!async_producer_commands.IsBound()) {
245 PERFETTO_DLOG(
246 "The Service tried to stop a data source but the remote Producer "
247 "has not yet initialized the connection");
248 return;
249 }
250 auto cmd = ipc::AsyncResult<GetAsyncCommandResponse>::Create();
251 cmd.set_has_more(true);
252 cmd->mutable_stop_data_source()->set_instance_id(dsid);
253 async_producer_commands.Resolve(std::move(cmd));
254}
255
256} // namespace perfetto