1 /* 2 * Copyright (C) 2019 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 #ifndef SRC_TRACING_INTERNAL_TRACING_MUXER_IMPL_H_ 18 #define SRC_TRACING_INTERNAL_TRACING_MUXER_IMPL_H_ 19 20 #include <stddef.h> 21 #include <stdint.h> 22 23 #include <array> 24 #include <atomic> 25 #include <bitset> 26 #include <functional> 27 #include <list> 28 #include <map> 29 #include <memory> 30 #include <set> 31 #include <utility> 32 #include <vector> 33 34 #include "perfetto/base/time.h" 35 #include "perfetto/ext/base/scoped_file.h" 36 #include "perfetto/ext/base/thread_checker.h" 37 #include "perfetto/ext/tracing/core/basic_types.h" 38 #include "perfetto/ext/tracing/core/consumer.h" 39 #include "perfetto/ext/tracing/core/producer.h" 40 #include "perfetto/ext/tracing/core/tracing_service.h" 41 #include "perfetto/tracing/backend_type.h" 42 #include "perfetto/tracing/core/data_source_descriptor.h" 43 #include "perfetto/tracing/core/forward_decls.h" 44 #include "perfetto/tracing/core/trace_config.h" 45 #include "perfetto/tracing/internal/basic_types.h" 46 #include "perfetto/tracing/internal/tracing_muxer.h" 47 #include "perfetto/tracing/tracing.h" 48 49 #include "protos/perfetto/common/interceptor_descriptor.gen.h" 50 51 namespace perfetto { 52 53 class ConsumerEndpoint; 54 class DataSourceBase; 55 class ProducerEndpoint; 56 class TraceWriterBase; 57 class TracingBackend; 58 class TracingSession; 59 struct TracingInitArgs; 60 61 namespace base { 62 class TaskRunner; 63 } 64 65 namespace shlib { 66 void ResetForTesting(); 67 } 68 69 namespace test { 70 class TracingMuxerImplInternalsForTest; 71 } 72 73 namespace internal { 74 75 struct DataSourceStaticState; 76 77 // This class acts as a bridge between the public API and the TracingBackend(s). 78 // It exposes a simplified view of the world to the API methods handling all the 79 // bookkeeping to map data source instances and trace writers to the various 80 // backends. It deals with N data sources, M backends (1 backend == 1 tracing 81 // service == 1 producer connection) and T concurrent tracing sessions. 82 // 83 // Handing data source registration and start/stop flows [producer side]: 84 // ---------------------------------------------------------------------- 85 // 1. The API client subclasses perfetto::DataSource and calls 86 // DataSource::Register<MyDataSource>(). In turn this calls into the 87 // TracingMuxer. 88 // 2. The tracing muxer iterates through all the backends (1 backend == 1 89 // service == 1 producer connection) and registers the data source on each 90 // backend. 91 // 3. When any (services behind a) backend starts tracing and requests to start 92 // that specific data source, the TracingMuxerImpl constructs a new instance 93 // of MyDataSource and calls the OnStart() method. 94 // 95 // Controlling trace and retrieving trace data [consumer side]: 96 // ------------------------------------------------------------ 97 // 1. The API client calls Tracing::NewTrace(), returns a RAII TracingSession 98 // object. 99 // 2. NewTrace() calls into internal::TracingMuxer(Impl). TracingMuxer 100 // subclasses the TracingSession object (TracingSessionImpl) and returns it. 101 // 3. The tracing muxer identifies the backend (according to the args passed to 102 // NewTrace), creates a new Consumer and connects to it. 103 // 4. When the API client calls Start()/Stop()/ReadTrace() methods, the 104 // TracingMuxer forwards them to the consumer associated to the 105 // TracingSession. Likewise for callbacks coming from the consumer-side of 106 // the service. 107 class TracingMuxerImpl : public TracingMuxer { 108 public: 109 // This is different than TracingSessionID because it's global across all 110 // backends. TracingSessionID is global only within the scope of one service. 111 using TracingSessionGlobalID = uint64_t; 112 113 struct RegisteredDataSource { 114 DataSourceDescriptor descriptor; 115 DataSourceFactory factory{}; 116 bool supports_multiple_instances = false; 117 bool requires_callbacks_under_lock = false; 118 bool no_flush = false; 119 DataSourceStaticState* static_state = nullptr; 120 }; 121 122 static void InitializeInstance(const TracingInitArgs&); 123 static void ResetForTesting(); 124 static void Shutdown(); 125 126 // TracingMuxer implementation. 127 bool RegisterDataSource(const DataSourceDescriptor&, 128 DataSourceFactory, 129 DataSourceParams, 130 bool no_flush, 131 DataSourceStaticState*) override; 132 void UpdateDataSourceDescriptor(const DataSourceDescriptor&, 133 const DataSourceStaticState*) override; 134 std::unique_ptr<TraceWriterBase> CreateTraceWriter( 135 DataSourceStaticState*, 136 uint32_t data_source_instance_index, 137 DataSourceState*, 138 BufferExhaustedPolicy buffer_exhausted_policy) override; 139 void DestroyStoppedTraceWritersForCurrentThread() override; 140 void RegisterInterceptor(const InterceptorDescriptor&, 141 InterceptorFactory, 142 InterceptorBase::TLSFactory, 143 InterceptorBase::TracePacketCallback) override; 144 145 void ActivateTriggers(const std::vector<std::string>&, uint32_t) override; 146 147 std::unique_ptr<TracingSession> CreateTracingSession( 148 BackendType, 149 TracingConsumerBackend* (*system_backend_factory)()); 150 std::unique_ptr<StartupTracingSession> CreateStartupTracingSession( 151 const TraceConfig& config, 152 Tracing::SetupStartupTracingOpts); 153 std::unique_ptr<StartupTracingSession> CreateStartupTracingSessionBlocking( 154 const TraceConfig& config, 155 Tracing::SetupStartupTracingOpts); 156 157 // Producer-side bookkeeping methods. 158 void UpdateDataSourcesOnAllBackends(); 159 void SetupDataSource(TracingBackendId, 160 uint32_t backend_connection_id, 161 DataSourceInstanceID, 162 const DataSourceConfig&); 163 void StartDataSource(TracingBackendId, DataSourceInstanceID); 164 void StopDataSource_AsyncBegin(TracingBackendId, DataSourceInstanceID); 165 void ClearDataSourceIncrementalState(TracingBackendId, DataSourceInstanceID); 166 void SyncProducersForTesting(); 167 168 // Consumer-side bookkeeping methods. 169 void SetupTracingSession(TracingSessionGlobalID, 170 const std::shared_ptr<TraceConfig>&, 171 base::ScopedFile trace_fd = base::ScopedFile()); 172 void StartTracingSession(TracingSessionGlobalID); 173 void CloneTracingSession(TracingSessionGlobalID, 174 TracingSession::CloneTraceArgs, 175 TracingSession::CloneTraceCallback); 176 void ChangeTracingSessionConfig(TracingSessionGlobalID, const TraceConfig&); 177 void StopTracingSession(TracingSessionGlobalID); 178 void DestroyTracingSession(TracingSessionGlobalID); 179 void FlushTracingSession(TracingSessionGlobalID, 180 uint32_t, 181 std::function<void(bool)>); 182 void ReadTracingSessionData( 183 TracingSessionGlobalID, 184 std::function<void(TracingSession::ReadTraceCallbackArgs)>); 185 void GetTraceStats(TracingSessionGlobalID, 186 TracingSession::GetTraceStatsCallback); 187 void QueryServiceState(TracingSessionGlobalID, 188 TracingSession::QueryServiceStateCallback); 189 190 // Sets the batching period to |batch_commits_duration_ms| on the backends 191 // with type |backend_type|. 192 void SetBatchCommitsDurationForTesting(uint32_t batch_commits_duration_ms, 193 BackendType backend_type); 194 195 // Enables direct SMB patching on the backends with type |backend_type| (see 196 // SharedMemoryArbiter::EnableDirectSMBPatching). Returns true if the 197 // operation succeeded for all backends with type |backend_type|, false 198 // otherwise. 199 bool EnableDirectSMBPatchingForTesting(BackendType backend_type); 200 201 void SetMaxProducerReconnectionsForTesting(uint32_t count); 202 203 private: 204 friend class test::TracingMuxerImplInternalsForTest; 205 friend void shlib::ResetForTesting(); 206 207 // For each TracingBackend we create and register one ProducerImpl instance. 208 // This talks to the producer-side of the service, gets start/stop requests 209 // from it and routes them to the registered data sources. 210 // One ProducerImpl == one backend == one tracing service. 211 // This class is needed to disambiguate callbacks coming from different 212 // services. TracingMuxerImpl can't directly implement the Producer interface 213 // because the Producer virtual methods don't allow to identify the service. 214 class ProducerImpl : public Producer { 215 public: 216 ProducerImpl(TracingMuxerImpl*, 217 TracingBackendId, 218 uint32_t shmem_batch_commits_duration_ms, 219 bool shmem_direct_patching_enabled); 220 ~ProducerImpl() override; 221 222 void Initialize(std::unique_ptr<ProducerEndpoint> endpoint); 223 void RegisterDataSource(const DataSourceDescriptor&, 224 DataSourceFactory, 225 DataSourceStaticState*); 226 void DisposeConnection(); 227 228 // perfetto::Producer implementation. 229 void OnConnect() override; 230 void OnDisconnect() override; 231 void OnTracingSetup() override; 232 void OnStartupTracingSetup() override; 233 void SetupDataSource(DataSourceInstanceID, 234 const DataSourceConfig&) override; 235 void StartDataSource(DataSourceInstanceID, 236 const DataSourceConfig&) override; 237 void StopDataSource(DataSourceInstanceID) override; 238 void Flush(FlushRequestID, 239 const DataSourceInstanceID*, 240 size_t, 241 FlushFlags) override; 242 void ClearIncrementalState(const DataSourceInstanceID*, size_t) override; 243 244 bool SweepDeadServices(); 245 void SendOnConnectTriggers(); 246 void NotifyFlushForDataSourceDone(DataSourceInstanceID, FlushRequestID); 247 248 PERFETTO_THREAD_CHECKER(thread_checker_) 249 TracingMuxerImpl* muxer_; 250 TracingBackendId const backend_id_; 251 bool connected_ = false; 252 bool did_setup_tracing_ = false; 253 bool did_setup_startup_tracing_ = false; 254 std::atomic<uint32_t> connection_id_{0}; 255 uint16_t last_startup_target_buffer_reservation_ = 0; 256 bool is_producer_provided_smb_ = false; 257 bool producer_provided_smb_failed_ = false; 258 259 const uint32_t shmem_batch_commits_duration_ms_ = 0; 260 const bool shmem_direct_patching_enabled_ = false; 261 262 // Set of data sources that have been actually registered on this producer. 263 // This can be a subset of the global |data_sources_|, because data sources 264 // can register before the producer is fully connected. 265 std::bitset<kMaxDataSources> registered_data_sources_{}; 266 267 // A collection of disconnected service endpoints. Since trace writers on 268 // arbitrary threads might continue writing data to disconnected services, 269 // we keep the old services around and periodically try to clean up ones 270 // that no longer have any writers (see SweepDeadServices). 271 std::list<std::shared_ptr<ProducerEndpoint>> dead_services_; 272 273 // Triggers that should be sent when the service connects (trigger_name, 274 // expiration). 275 std::list<std::pair<std::string, base::TimeMillis>> on_connect_triggers_; 276 277 std::map<FlushRequestID, std::set<DataSourceInstanceID>> pending_flushes_; 278 279 // The currently active service endpoint is maintained as an atomic shared 280 // pointer so it won't get deleted from underneath threads that are creating 281 // trace writers. At any given time one endpoint can be shared (and thus 282 // kept alive) by the |service_| pointer, an entry in |dead_services_| and 283 // as a pointer on the stack in CreateTraceWriter() (on an arbitrary 284 // thread). The endpoint is never shared outside ProducerImpl itself. 285 // 286 // WARNING: Any *write* access to this variable or any *read* access from a 287 // non-muxer thread must be done through std::atomic_{load,store} to avoid 288 // data races. 289 std::shared_ptr<ProducerEndpoint> service_; // Keep last. 290 }; 291 292 // For each TracingSession created by the API client (Tracing::NewTrace() we 293 // create and register one ConsumerImpl instance. 294 // This talks to the consumer-side of the service, gets end-of-trace and 295 // on-trace-data callbacks and routes them to the API client callbacks. 296 // This class is needed to disambiguate callbacks coming from different 297 // tracing sessions. 298 class ConsumerImpl : public Consumer { 299 public: 300 ConsumerImpl(TracingMuxerImpl*, BackendType, TracingSessionGlobalID); 301 ~ConsumerImpl() override; 302 303 void Initialize(std::unique_ptr<ConsumerEndpoint> endpoint); 304 305 // perfetto::Consumer implementation. 306 void OnConnect() override; 307 void OnDisconnect() override; 308 void OnTracingDisabled(const std::string& error) override; 309 void OnTraceData(std::vector<TracePacket>, bool has_more) override; 310 void OnDetach(bool success) override; 311 void OnAttach(bool success, const TraceConfig&) override; 312 void OnTraceStats(bool success, const TraceStats&) override; 313 void OnObservableEvents(const ObservableEvents&) override; 314 void OnSessionCloned(const OnSessionClonedArgs&) override; 315 316 void NotifyStartComplete(); 317 void NotifyError(const TracingError&); 318 void NotifyStopComplete(); 319 320 // Will eventually inform the |muxer_| when it is safe to remove |this|. 321 void Disconnect(); 322 323 TracingMuxerImpl* muxer_; 324 BackendType const backend_type_; 325 TracingSessionGlobalID const session_id_; 326 bool connected_ = false; 327 328 // This is to handle the case where the Setup call from the API client 329 // arrives before the consumer has connected. In this case we keep around 330 // the config and check if we have it after connection. 331 bool start_pending_ = false; 332 333 // Similarly if the session is stopped before the consumer was connected, we 334 // need to wait until the session has started before stopping it. 335 bool stop_pending_ = false; 336 337 // Similarly we need to buffer a call to get trace statistics if the 338 // consumer wasn't connected yet. 339 bool get_trace_stats_pending_ = false; 340 341 // Similarly we need to buffer a session cloning args if the session is 342 // cloning another sesison before the consumer was connected. 343 std::optional<ConsumerEndpoint::CloneSessionArgs> session_to_clone_; 344 345 // Whether this session was already stopped. This will happen in response to 346 // Stop{,Blocking}, but also if the service stops the session for us 347 // automatically (e.g., when there are no data sources). 348 bool stopped_ = false; 349 350 // shared_ptr because it's posted across threads. This is to avoid copying 351 // it more than once. 352 std::shared_ptr<TraceConfig> trace_config_; 353 base::ScopedFile trace_fd_; 354 355 // If the API client passes a callback to start, we should invoke this when 356 // NotifyStartComplete() is invoked. 357 std::function<void()> start_complete_callback_; 358 359 // An internal callback used to implement StartBlocking(). 360 std::function<void()> blocking_start_complete_callback_; 361 362 // If the API client passes a callback to get notification about the 363 // errors, we should invoke this when NotifyError() is invoked. 364 std::function<void(TracingError)> error_callback_; 365 366 // If the API client passes a callback to stop, we should invoke this when 367 // OnTracingDisabled() is invoked. 368 std::function<void()> stop_complete_callback_; 369 370 // An internal callback used to implement StopBlocking(). 371 std::function<void()> blocking_stop_complete_callback_; 372 373 // Callback for a pending call to CloneTrace(). 374 TracingSession::CloneTraceCallback clone_trace_callback_; 375 376 // Callback passed to ReadTrace(). 377 std::function<void(TracingSession::ReadTraceCallbackArgs)> 378 read_trace_callback_; 379 380 // Callback passed to GetTraceStats(). 381 TracingSession::GetTraceStatsCallback get_trace_stats_callback_; 382 383 // Callback for a pending call to QueryServiceState(). 384 TracingSession::QueryServiceStateCallback query_service_state_callback_; 385 386 // The states of all data sources in this tracing session. |true| means the 387 // data source has started tracing. 388 using DataSourceHandle = std::pair<std::string, std::string>; 389 std::map<DataSourceHandle, bool> data_source_states_; 390 391 std::unique_ptr<ConsumerEndpoint> service_; // Keep before last. 392 PERFETTO_THREAD_CHECKER(thread_checker_) // Keep last. 393 }; 394 395 // This object is returned to API clients when they call 396 // Tracing::CreateTracingSession(). 397 class TracingSessionImpl : public TracingSession { 398 public: 399 TracingSessionImpl(TracingMuxerImpl*, TracingSessionGlobalID, BackendType); 400 ~TracingSessionImpl() override; 401 void Setup(const TraceConfig&, int fd) override; 402 void Start() override; 403 void StartBlocking() override; 404 void CloneTrace(CloneTraceArgs args, CloneTraceCallback) override; 405 void SetOnStartCallback(std::function<void()>) override; 406 void SetOnErrorCallback(std::function<void(TracingError)>) override; 407 void Stop() override; 408 void StopBlocking() override; 409 void Flush(std::function<void(bool)>, uint32_t timeout_ms) override; 410 void ReadTrace(ReadTraceCallback) override; 411 void SetOnStopCallback(std::function<void()>) override; 412 void GetTraceStats(GetTraceStatsCallback) override; 413 void QueryServiceState(QueryServiceStateCallback) override; 414 void ChangeTraceConfig(const TraceConfig&) override; 415 416 private: 417 TracingMuxerImpl* const muxer_; 418 TracingSessionGlobalID const session_id_; 419 BackendType const backend_type_; 420 }; 421 422 // This object is returned to API clients when they call 423 // Tracing::SetupStartupTracing(). 424 class StartupTracingSessionImpl : public StartupTracingSession { 425 public: 426 StartupTracingSessionImpl(TracingMuxerImpl*, 427 TracingSessionGlobalID, 428 BackendType); 429 ~StartupTracingSessionImpl() override; 430 void Abort() override; 431 void AbortBlocking() override; 432 433 private: 434 TracingMuxerImpl* const muxer_; 435 TracingSessionGlobalID const session_id_; 436 BackendType backend_type_; 437 }; 438 439 struct RegisteredInterceptor { 440 protos::gen::InterceptorDescriptor descriptor; 441 InterceptorFactory factory{}; 442 InterceptorBase::TLSFactory tls_factory{}; 443 InterceptorBase::TracePacketCallback packet_callback{}; 444 }; 445 446 struct RegisteredStartupSession { 447 TracingSessionID session_id = 0; 448 int num_unbound_data_sources = 0; 449 450 bool is_aborting = false; 451 int num_aborting_data_sources = 0; 452 453 std::function<void()> on_aborted; 454 std::function<void()> on_adopted; 455 }; 456 457 struct RegisteredProducerBackend { 458 // Backends are supposed to have static lifetime. 459 TracingProducerBackend* backend = nullptr; 460 TracingBackendId id = 0; 461 BackendType type{}; 462 463 TracingBackend::ConnectProducerArgs producer_conn_args; 464 std::unique_ptr<ProducerImpl> producer; 465 466 std::vector<RegisteredStartupSession> startup_sessions; 467 }; 468 469 struct RegisteredConsumerBackend { 470 // Backends are supposed to have static lifetime. 471 TracingConsumerBackend* backend = nullptr; 472 BackendType type{}; 473 // The calling code can request more than one concurrently active tracing 474 // session for the same backend. We need to create one consumer per session. 475 std::vector<std::unique_ptr<ConsumerImpl>> consumers; 476 }; 477 478 void UpdateDataSourceOnAllBackends(RegisteredDataSource& rds, 479 bool is_changed); 480 explicit TracingMuxerImpl(const TracingInitArgs&); 481 void Initialize(const TracingInitArgs& args); 482 void AddBackends(const TracingInitArgs& args); 483 void AddConsumerBackend(TracingConsumerBackend* backend, BackendType type); 484 void AddProducerBackend(TracingProducerBackend* backend, 485 BackendType type, 486 const TracingInitArgs& args); 487 ConsumerImpl* FindConsumer(TracingSessionGlobalID session_id); 488 std::pair<ConsumerImpl*, RegisteredConsumerBackend*> FindConsumerAndBackend( 489 TracingSessionGlobalID session_id); 490 RegisteredProducerBackend* FindProducerBackendById(TracingBackendId id); 491 RegisteredProducerBackend* FindProducerBackendByType(BackendType type); 492 RegisteredConsumerBackend* FindConsumerBackendByType(BackendType type); 493 void InitializeConsumer(TracingSessionGlobalID session_id); 494 void OnConsumerDisconnected(ConsumerImpl* consumer); 495 void OnProducerDisconnected(ProducerImpl* producer); 496 // Test only method. 497 void SweepDeadBackends(); 498 499 struct FindDataSourceRes { 500 FindDataSourceRes() = default; FindDataSourceResFindDataSourceRes501 FindDataSourceRes(DataSourceStaticState* a, 502 DataSourceState* b, 503 uint32_t c, 504 bool d) 505 : static_state(a), 506 internal_state(b), 507 instance_idx(c), 508 requires_callbacks_under_lock(d) {} 509 explicit operator bool() const { return !!internal_state; } 510 511 DataSourceStaticState* static_state = nullptr; 512 DataSourceState* internal_state = nullptr; 513 uint32_t instance_idx = 0; 514 bool requires_callbacks_under_lock = false; 515 }; 516 FindDataSourceRes FindDataSource(TracingBackendId, DataSourceInstanceID); 517 518 FindDataSourceRes SetupDataSourceImpl( 519 const RegisteredDataSource&, 520 TracingBackendId, 521 uint32_t backend_connection_id, 522 DataSourceInstanceID, 523 const DataSourceConfig&, 524 TracingSessionGlobalID startup_session_id); 525 void StartDataSourceImpl(const FindDataSourceRes&); 526 void StopDataSource_AsyncBeginImpl(const FindDataSourceRes&); 527 void StopDataSource_AsyncEnd(TracingBackendId, 528 uint32_t backend_connection_id, 529 DataSourceInstanceID, 530 const FindDataSourceRes&); 531 bool FlushDataSource_AsyncBegin(TracingBackendId, 532 DataSourceInstanceID, 533 FlushRequestID, 534 FlushFlags); 535 void FlushDataSource_AsyncEnd(TracingBackendId, 536 uint32_t backend_connection_id, 537 DataSourceInstanceID, 538 const FindDataSourceRes&, 539 FlushRequestID); 540 void AbortStartupTracingSession(TracingSessionGlobalID, BackendType); 541 // When ResetForTesting() is executed, `cb` will be called on the calling 542 // thread and on the muxer thread. 543 void AppendResetForTestingCallback(std::function<void()> cb); 544 545 // WARNING: If you add new state here, be sure to update ResetForTesting. 546 std::unique_ptr<base::TaskRunner> task_runner_; 547 std::vector<RegisteredDataSource> data_sources_; 548 // These lists can only have one backend per BackendType. The elements are 549 // sorted by BackendType priority (see BackendTypePriority). They always 550 // contain a fake low-priority kUnspecifiedBackend at the end. 551 std::list<RegisteredProducerBackend> producer_backends_; 552 std::list<RegisteredConsumerBackend> consumer_backends_; 553 std::vector<RegisteredInterceptor> interceptors_; 554 TracingPolicy* policy_ = nullptr; 555 556 // Learn more at TracingInitArgs::supports_multiple_data_source_instances 557 bool supports_multiple_data_source_instances_ = true; 558 559 std::atomic<TracingSessionGlobalID> next_tracing_session_id_{}; 560 std::atomic<uint32_t> next_data_source_index_{}; 561 uint32_t muxer_id_for_testing_{}; 562 563 // Maximum number of times we will try to reconnect producer backend. 564 // Should only be modified for testing purposes. 565 std::atomic<uint32_t> max_producer_reconnections_{100u}; 566 567 // Test only member. 568 // After ResetForTesting() is called, holds tracing backends which needs to be 569 // kept alive until all inbound references have gone away. See 570 // SweepDeadBackends(). 571 std::list<RegisteredProducerBackend> dead_backends_; 572 573 // Test only member. 574 // Executes these cleanup functions on the calling thread and on the muxer 575 // thread when ResetForTesting() is called. 576 std::list<std::function<void()>> reset_callbacks_; 577 578 PERFETTO_THREAD_CHECKER(thread_checker_) 579 }; 580 581 } // namespace internal 582 } // namespace perfetto 583 584 #endif // SRC_TRACING_INTERNAL_TRACING_MUXER_IMPL_H_ 585