| /* | 
 |  * Copyright (C) 2019 The Android Open Source Project | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 |  | 
 | #ifndef SRC_TRACING_INTERNAL_TRACING_MUXER_IMPL_H_ | 
 | #define SRC_TRACING_INTERNAL_TRACING_MUXER_IMPL_H_ | 
 |  | 
 | #include <stddef.h> | 
 | #include <stdint.h> | 
 |  | 
 | #include <array> | 
 | #include <atomic> | 
 | #include <bitset> | 
 | #include <map> | 
 | #include <memory> | 
 | #include <vector> | 
 |  | 
 | #include "perfetto/ext/base/scoped_file.h" | 
 | #include "perfetto/ext/base/thread_checker.h" | 
 | #include "perfetto/ext/tracing/core/basic_types.h" | 
 | #include "perfetto/ext/tracing/core/consumer.h" | 
 | #include "perfetto/ext/tracing/core/producer.h" | 
 | #include "perfetto/tracing/core/data_source_descriptor.h" | 
 | #include "perfetto/tracing/core/trace_config.h" | 
 | #include "perfetto/tracing/internal/basic_types.h" | 
 | #include "perfetto/tracing/internal/tracing_muxer.h" | 
 | #include "perfetto/tracing/tracing.h" | 
 |  | 
 | namespace perfetto { | 
 |  | 
 | class ConsumerEndpoint; | 
 | class DataSourceBase; | 
 | class DataSourceConfig; | 
 | class ProducerEndpoint; | 
 | class TraceWriterBase; | 
 | class TracingBackend; | 
 | class TracingSession; | 
 | struct TracingInitArgs; | 
 |  | 
 | namespace base { | 
 | class TaskRunner; | 
 | } | 
 |  | 
 | namespace internal { | 
 |  | 
 | struct DataSourceStaticState; | 
 |  | 
 | // This class acts as a bridge between the public API and the TracingBackend(s). | 
 | // It exposes a simplified view of the world to the API methods handling all the | 
 | // bookkeeping to map data source instances and trace writers to the various | 
 | // backends. It deals with N data sources, M backends (1 backend == 1 tracing | 
 | // service == 1 producer connection) and T concurrent tracing sessions. | 
 | // | 
 | // Handing data source registration and start/stop flows [producer side]: | 
 | // ---------------------------------------------------------------------- | 
 | // 1. The API client subclasses perfetto::DataSource and calls | 
 | //    DataSource::Register<MyDataSource>(). In turn this calls into the | 
 | //    TracingMuxer. | 
 | // 2. The tracing muxer iterates through all the backends (1 backend == 1 | 
 | //    service == 1 producer connection) and registers the data source on each | 
 | //    backend. | 
 | // 3. When any (services behind a) backend starts tracing and requests to start | 
 | //    that specific data source, the TracingMuxerImpl constructs a new instance | 
 | //    of MyDataSource and calls the OnStart() method. | 
 | // | 
 | // Controlling trace and retrieving trace data [consumer side]: | 
 | // ------------------------------------------------------------ | 
 | // 1. The API client calls Tracing::NewTrace(), returns a RAII TracingSession | 
 | //    object. | 
 | // 2. NewTrace() calls into internal::TracingMuxer(Impl). TracingMuxer | 
 | //    subclasses the TracingSession object (TracingSessionImpl) and returns it. | 
 | // 3. The tracing muxer identifies the backend (according to the args passed to | 
 | //    NewTrace), creates a new Consumer and connects to it. | 
 | // 4. When the API client calls Start()/Stop()/ReadTrace() methods, the | 
 | //    TracingMuxer forwards them to the consumer associated to the | 
 | //    TracingSession. Likewise for callbacks coming from the consumer-side of | 
 | //    the service. | 
 | class TracingMuxerImpl : public TracingMuxer { | 
 |  public: | 
 |   // This is different than TracingSessionID because it's global across all | 
 |   // backends. TracingSessionID is global only within the scope of one service. | 
 |   using TracingSessionGlobalID = uint64_t; | 
 |  | 
 |   static void InitializeInstance(const TracingInitArgs&); | 
 |  | 
 |   // TracingMuxer implementation. | 
 |   bool RegisterDataSource(const DataSourceDescriptor&, | 
 |                           DataSourceFactory, | 
 |                           DataSourceStaticState*) override; | 
 |   std::unique_ptr<TraceWriterBase> CreateTraceWriter( | 
 |       DataSourceState*, | 
 |       BufferExhaustedPolicy buffer_exhausted_policy) override; | 
 |   void DestroyStoppedTraceWritersForCurrentThread() override; | 
 |  | 
 |   std::unique_ptr<TracingSession> CreateTracingSession(BackendType); | 
 |  | 
 |   // Producer-side bookkeeping methods. | 
 |   void UpdateDataSourcesOnAllBackends(); | 
 |   void SetupDataSource(TracingBackendId, | 
 |                        DataSourceInstanceID, | 
 |                        const DataSourceConfig&); | 
 |   void StartDataSource(TracingBackendId, DataSourceInstanceID); | 
 |   void StopDataSource_AsyncBegin(TracingBackendId, DataSourceInstanceID); | 
 |   void StopDataSource_AsyncEnd(TracingBackendId, DataSourceInstanceID); | 
 |  | 
 |   // Consumer-side bookkeeping methods. | 
 |   void SetupTracingSession(TracingSessionGlobalID, | 
 |                            const std::shared_ptr<TraceConfig>&, | 
 |                            base::ScopedFile trace_fd = base::ScopedFile()); | 
 |   void StartTracingSession(TracingSessionGlobalID); | 
 |   void StopTracingSession(TracingSessionGlobalID); | 
 |   void DestroyTracingSession(TracingSessionGlobalID); | 
 |   void ReadTracingSessionData( | 
 |       TracingSessionGlobalID, | 
 |       std::function<void(TracingSession::ReadTraceCallbackArgs)>); | 
 |  | 
 |  private: | 
 |   // For each TracingBackend we create and register one ProducerImpl instance. | 
 |   // This talks to the producer-side of the service, gets start/stop requests | 
 |   // from it and routes them to the registered data sources. | 
 |   // One ProducerImpl == one backend == one tracing service. | 
 |   // This class is needed to disambiguate callbacks coming from different | 
 |   // services. TracingMuxerImpl can't directly implement the Producer interface | 
 |   // because the Producer virtual methods don't allow to identify the service. | 
 |   class ProducerImpl : public Producer { | 
 |    public: | 
 |     ProducerImpl(TracingMuxerImpl*, TracingBackendId); | 
 |     ~ProducerImpl() override; | 
 |  | 
 |     void Initialize(std::unique_ptr<ProducerEndpoint> endpoint); | 
 |     void RegisterDataSource(const DataSourceDescriptor&, | 
 |                             DataSourceFactory, | 
 |                             DataSourceStaticState*); | 
 |  | 
 |     // perfetto::Producer implementation. | 
 |     void OnConnect() override; | 
 |     void OnDisconnect() override; | 
 |     void OnTracingSetup() override; | 
 |     void SetupDataSource(DataSourceInstanceID, | 
 |                          const DataSourceConfig&) override; | 
 |     void StartDataSource(DataSourceInstanceID, | 
 |                          const DataSourceConfig&) override; | 
 |     void StopDataSource(DataSourceInstanceID) override; | 
 |     void Flush(FlushRequestID, const DataSourceInstanceID*, size_t) override; | 
 |     void ClearIncrementalState(const DataSourceInstanceID*, size_t) override; | 
 |  | 
 |     PERFETTO_THREAD_CHECKER(thread_checker_) | 
 |     TracingMuxerImpl* const muxer_; | 
 |     TracingBackendId const backend_id_; | 
 |     bool connected_ = false; | 
 |  | 
 |     // Set of data sources that have been actually registered on this producer. | 
 |     // This can be a subset of the global |data_sources_|, because data sources | 
 |     // can register before the producer is fully connected. | 
 |     std::bitset<kMaxDataSources> registered_data_sources_{}; | 
 |  | 
 |     std::unique_ptr<ProducerEndpoint> service_;  // Keep last. | 
 |   }; | 
 |  | 
 |   // For each TracingSession created by the API client (Tracing::NewTrace() we | 
 |   // create and register one ConsumerImpl instance. | 
 |   // This talks to the consumer-side of the service, gets end-of-trace and | 
 |   // on-trace-data callbacks and routes them to the API client callbacks. | 
 |   // This class is needed to disambiguate callbacks coming from different | 
 |   // tracing sessions. | 
 |   class ConsumerImpl : public Consumer { | 
 |    public: | 
 |     ConsumerImpl(TracingMuxerImpl*, TracingBackendId, TracingSessionGlobalID); | 
 |     ~ConsumerImpl() override; | 
 |  | 
 |     void Initialize(std::unique_ptr<ConsumerEndpoint> endpoint); | 
 |  | 
 |     // perfetto::Consumer implementation. | 
 |     void OnConnect() override; | 
 |     void OnDisconnect() override; | 
 |     void OnTracingDisabled() override; | 
 |     void OnTraceData(std::vector<TracePacket>, bool has_more) override; | 
 |     void OnDetach(bool success) override; | 
 |     void OnAttach(bool success, const TraceConfig&) override; | 
 |     void OnTraceStats(bool success, const TraceStats&) override; | 
 |     void OnObservableEvents(const ObservableEvents&) override; | 
 |  | 
 |     void NotifyStartComplete(); | 
 |     void NotifyStopComplete(); | 
 |  | 
 |     // Will eventually inform the |muxer_| when it is safe to remove |this|. | 
 |     void Disconnect(); | 
 |  | 
 |     TracingMuxerImpl* const muxer_; | 
 |     TracingBackendId const backend_id_; | 
 |     TracingSessionGlobalID const session_id_; | 
 |     bool connected_ = false; | 
 |  | 
 |     // This is to handle the case where the Setup call from the API client | 
 |     // arrives before the consumer has connected. In this case we keep around | 
 |     // the config and check if we have it after connection. | 
 |     bool start_pending_ = false; | 
 |  | 
 |     // Similarly if the session is stopped before the consumer was connected, we | 
 |     // need to wait until the session has started before stopping it. | 
 |     bool stop_pending_ = false; | 
 |  | 
 |     // Whether this session was already stopped. This will happen in response to | 
 |     // Stop{,Blocking}, but also if the service stops the session for us | 
 |     // automatically (e.g., when there are no data sources). | 
 |     bool stopped_ = false; | 
 |  | 
 |     // shared_ptr because it's posted across threads. This is to avoid copying | 
 |     // it more than once. | 
 |     std::shared_ptr<TraceConfig> trace_config_; | 
 |     base::ScopedFile trace_fd_; | 
 |  | 
 |     // An internal callback used to implement StartBlocking(). | 
 |     std::function<void()> blocking_start_complete_callback_; | 
 |  | 
 |     // If the API client passes a callback to stop, we should invoke this when | 
 |     // OnTracingDisabled() is invoked. | 
 |     std::function<void()> stop_complete_callback_; | 
 |  | 
 |     // An internal callback used to implement StopBlocking(). | 
 |     std::function<void()> blocking_stop_complete_callback_; | 
 |  | 
 |     // Callback passed to ReadTrace(). | 
 |     std::function<void(TracingSession::ReadTraceCallbackArgs)> | 
 |         read_trace_callback_; | 
 |  | 
 |     // The states of all data sources in this tracing session. |true| means the | 
 |     // data source has started tracing. | 
 |     using DataSourceHandle = std::pair<std::string, std::string>; | 
 |     std::map<DataSourceHandle, bool> data_source_states_; | 
 |  | 
 |     std::unique_ptr<ConsumerEndpoint> service_;  // Keep before last. | 
 |     PERFETTO_THREAD_CHECKER(thread_checker_)     // Keep last. | 
 |   }; | 
 |  | 
 |   // This object is returned to API clients when they call | 
 |   // Tracing::CreateTracingSession(). | 
 |   class TracingSessionImpl : public TracingSession { | 
 |    public: | 
 |     TracingSessionImpl(TracingMuxerImpl*, TracingSessionGlobalID); | 
 |     ~TracingSessionImpl() override; | 
 |     void Setup(const TraceConfig&, int fd) override; | 
 |     void Start() override; | 
 |     void StartBlocking() override; | 
 |     void Stop() override; | 
 |     void StopBlocking() override; | 
 |     void ReadTrace(ReadTraceCallback) override; | 
 |     void SetOnStopCallback(std::function<void()>) override; | 
 |  | 
 |    private: | 
 |     TracingMuxerImpl* const muxer_; | 
 |     TracingSessionGlobalID const session_id_; | 
 |   }; | 
 |  | 
 |   struct RegisteredDataSource { | 
 |     DataSourceDescriptor descriptor; | 
 |     DataSourceFactory factory{}; | 
 |     DataSourceStaticState* static_state = nullptr; | 
 |   }; | 
 |  | 
 |   struct RegisteredBackend { | 
 |     // Backends are supposed to have static lifetime. | 
 |     TracingBackend* backend = nullptr; | 
 |     TracingBackendId id = 0; | 
 |     BackendType type{}; | 
 |  | 
 |     std::unique_ptr<ProducerImpl> producer; | 
 |  | 
 |     // The calling code can request more than one concurrently active tracing | 
 |     // session for the same backend. We need to create one consumer per session. | 
 |     std::vector<std::unique_ptr<ConsumerImpl>> consumers; | 
 |   }; | 
 |  | 
 |   explicit TracingMuxerImpl(const TracingInitArgs&); | 
 |   void Initialize(const TracingInitArgs& args); | 
 |   ConsumerImpl* FindConsumer(TracingSessionGlobalID session_id); | 
 |   void OnConsumerDisconnected(ConsumerImpl* consumer); | 
 |  | 
 |   struct FindDataSourceRes { | 
 |     FindDataSourceRes() = default; | 
 |     FindDataSourceRes(DataSourceStaticState* a, DataSourceState* b, uint32_t c) | 
 |         : static_state(a), internal_state(b), instance_idx(c) {} | 
 |     explicit operator bool() const { return !!internal_state; } | 
 |  | 
 |     DataSourceStaticState* static_state = nullptr; | 
 |     DataSourceState* internal_state = nullptr; | 
 |     uint32_t instance_idx = 0; | 
 |   }; | 
 |   FindDataSourceRes FindDataSource(TracingBackendId, DataSourceInstanceID); | 
 |  | 
 |   std::unique_ptr<base::TaskRunner> task_runner_; | 
 |   std::vector<RegisteredDataSource> data_sources_; | 
 |   std::vector<RegisteredBackend> backends_; | 
 |  | 
 |   std::atomic<TracingSessionGlobalID> next_tracing_session_id_{}; | 
 |  | 
 |   PERFETTO_THREAD_CHECKER(thread_checker_) | 
 | }; | 
 |  | 
 | }  // namespace internal | 
 | }  // namespace perfetto | 
 |  | 
 | #endif  // SRC_TRACING_INTERNAL_TRACING_MUXER_IMPL_H_ |