Home | History | Annotate | Download | only in service
      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