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 36 ProducerIPCService::ProducerIPCService(TracingService* core_service) 37 : core_service_(core_service), weak_ptr_factory_(this) {} 38 39 ProducerIPCService::~ProducerIPCService() = default; 40 41 ProducerIPCService::RemoteProducer* 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. 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. 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. 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. 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 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 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 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 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 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 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 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. 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). 326 void ProducerIPCService::RemoteProducer::OnDisconnect() {} 327 328 // Invoked by the |core_service_| business logic when it wants to create a new 329 // data source. 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. 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 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 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 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 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