// Copyright 2015 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_TRACE_EVENT_TRACE_LOG_H_ #define BASE_TRACE_EVENT_TRACE_LOG_H_ #include #include #include #include #include #include #include #include #include #include "base/base_export.h" #include "base/containers/stack.h" #include "base/gtest_prod_util.h" #include "base/memory/raw_ptr.h" #include "base/memory/scoped_refptr.h" #include "base/no_destructor.h" #include "base/task/single_thread_task_runner.h" #include "base/threading/platform_thread.h" #include "base/time/time_override.h" #include "base/trace_event/category_registry.h" #include "base/trace_event/memory_dump_provider.h" #include "base/trace_event/trace_config.h" #include "base/trace_event/trace_event_impl.h" #include "build/build_config.h" #if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) #include "third_party/perfetto/include/perfetto/tracing/core/trace_config.h" namespace perfetto { namespace trace_processor { class TraceProcessorStorage; } // namespace trace_processor } // namespace perfetto #endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) namespace base { class RefCountedString; namespace trace_event { struct TraceCategory; class TraceBuffer; class TraceBufferChunk; class TraceEvent; class TraceEventMemoryOverhead; class JsonStringOutputWriter; struct BASE_EXPORT TraceLogStatus { TraceLogStatus(); ~TraceLogStatus(); uint32_t event_capacity; uint32_t event_count; }; class BASE_EXPORT TraceLog : #if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) public perfetto::TrackEventSessionObserver, #endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) public MemoryDumpProvider { public: class ThreadLocalEventBuffer; // Argument passed to TraceLog::SetEnabled. enum Mode : uint8_t { // Enables normal tracing (recording trace events in the trace buffer). // This is the only tracing mode supported now. // TODO(khokhlov): Clean up all uses of tracing mode and remove this enum // completely. RECORDING_MODE = 1 << 0, }; static TraceLog* GetInstance(); TraceLog(const TraceLog&) = delete; TraceLog& operator=(const TraceLog&) = delete; // Retrieves a copy (for thread-safety) of the current TraceConfig. TraceConfig GetCurrentTraceConfig() const; // Initializes the thread-local event buffer, if not already initialized and // if the current thread supports that (has a message loop). void InitializeThreadLocalEventBufferIfSupported(); // See TraceConfig comments for details on how to control which categories // will be traced. Only RECORDING_MODE is supported. void SetEnabled(const TraceConfig& trace_config, uint8_t modes_to_enable); #if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) // Enable tracing using a customized Perfetto trace config. This allows, for // example, enabling additional data sources and enabling protobuf output // instead of the legacy JSON trace format. void SetEnabled(const TraceConfig& trace_config, const perfetto::TraceConfig& perfetto_config); #endif // Disables tracing for all categories. Only RECORDING_MODE is supported. void SetDisabled(); void SetDisabled(uint8_t modes_to_disable); // Returns true if TraceLog is enabled on recording mode. // Note: Returns false even if FILTERING_MODE is enabled. bool IsEnabled() { #if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) // In SDK build we return true as soon as the datasource has been set up and // we know the config. This doesn't necessarily mean that the tracing has // already started. // Note that TrackEvent::IsEnabled() can be true even earlier, before the // OnSetup call, so we can't guarantee that we know the config by the time // TrackEvent::IsEnabled() is true. AutoLock lock(track_event_lock_); return track_event_sessions_.size() > 0; #else // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) AutoLock lock(lock_); return enabled_; #endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) } // The number of times we have begun recording traces. If tracing is off, // returns -1. If tracing is on, then it returns the number of times we have // recorded a trace. By watching for this number to increment, you can // passively discover when a new trace has begun. This is then used to // implement the TRACE_EVENT_IS_NEW_TRACE() primitive. int GetNumTracesRecorded(); // Enabled state listeners give a callback when tracing is enabled or // disabled. This can be used to tie into other library's tracing systems // on-demand. class BASE_EXPORT EnabledStateObserver { public: virtual ~EnabledStateObserver() = default; // Called just after the tracing system becomes enabled, outside of the // |lock_|. TraceLog::IsEnabled() is true at this point. virtual void OnTraceLogEnabled() = 0; // Called just after the tracing system disables, outside of the |lock_|. // TraceLog::IsEnabled() is false at this point. virtual void OnTraceLogDisabled() = 0; }; // Adds an observer. Cannot be called from within the observer callback. void AddEnabledStateObserver(EnabledStateObserver* listener); // Removes an observer. Cannot be called from within the observer callback. void RemoveEnabledStateObserver(EnabledStateObserver* listener); // Adds an observer that is owned by TraceLog. This is useful for agents that // implement tracing feature that needs to stay alive as long as TraceLog // does. void AddOwnedEnabledStateObserver( std::unique_ptr listener); bool HasEnabledStateObserver(EnabledStateObserver* listener) const; // Asynchronous enabled state listeners. When tracing is enabled or disabled, // for each observer, a task for invoking its appropriate callback is posted // to the `SequencedTaskRunner` from which AddAsyncEnabledStateObserver() was // called. This allows the observer to be safely destroyed, provided that it // happens on the same `SequencedTaskRunner` that invoked // AddAsyncEnabledStateObserver(). class BASE_EXPORT AsyncEnabledStateObserver { public: virtual ~AsyncEnabledStateObserver() = default; // Posted just after the tracing system becomes enabled, outside |lock_|. // TraceLog::IsEnabled() is true at this point. virtual void OnTraceLogEnabled() = 0; // Posted just after the tracing system becomes disabled, outside |lock_|. // TraceLog::IsEnabled() is false at this point. virtual void OnTraceLogDisabled() = 0; }; // TODO(oysteine): This API originally needed to use WeakPtrs as the observer // list was copied under the global trace lock, but iterated over outside of // that lock so that observers could add tracing. The list is now protected by // its own lock, so this can be changed to a raw ptr. void AddAsyncEnabledStateObserver( WeakPtr listener); void RemoveAsyncEnabledStateObserver(AsyncEnabledStateObserver* listener); bool HasAsyncEnabledStateObserver(AsyncEnabledStateObserver* listener) const; // Observers that are notified when incremental state is cleared. This only // happens when tracing using the perfetto backend. class BASE_EXPORT IncrementalStateObserver { public: virtual ~IncrementalStateObserver() = default; // Called just after the tracing system has cleared incremental state, while // a tracing session is active. virtual void OnIncrementalStateCleared() = 0; }; // Adds an observer. Cannot be called from within the observer callback. void AddIncrementalStateObserver(IncrementalStateObserver* listener); // Removes an observer. Cannot be called from within the observer callback. void RemoveIncrementalStateObserver(IncrementalStateObserver* listener); TraceLogStatus GetStatus() const; #if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) bool BufferIsFull() const; #endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) // Computes an estimate of the size of the TraceLog including all the retained // objects. void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead); void SetArgumentFilterPredicate( const ArgumentFilterPredicate& argument_filter_predicate); ArgumentFilterPredicate GetArgumentFilterPredicate() const; void SetMetadataFilterPredicate( const MetadataFilterPredicate& metadata_filter_predicate); MetadataFilterPredicate GetMetadataFilterPredicate() const; void SetRecordHostAppPackageName(bool record_host_app_package_name); bool ShouldRecordHostAppPackageName() const; // Flush all collected events to the given output callback. The callback will // be called one or more times either synchronously or asynchronously from // the current thread with IPC-bite-size chunks. The string format is // undefined. Use TraceResultBuffer to convert one or more trace strings to // JSON. The callback can be null if the caller doesn't want any data. // Due to the implementation of thread-local buffers, flush can't be // done when tracing is enabled. If called when tracing is enabled, the // callback will be called directly with (empty_string, false) to indicate // the end of this unsuccessful flush. Flush does the serialization // on the same thread if the caller doesn't set use_worker_thread explicitly. using OutputCallback = base::RepeatingCallback&, bool has_more_events)>; void Flush(const OutputCallback& cb, bool use_worker_thread = false); // Cancels tracing and discards collected data. void CancelTracing(const OutputCallback& cb); using AddTraceEventOverrideFunction = void (*)(TraceEvent*, bool thread_will_flush, TraceEventHandle* handle); using OnFlushFunction = void (*)(); using UpdateDurationFunction = void (*)(const unsigned char* category_group_enabled, const char* name, TraceEventHandle handle, PlatformThreadId thread_id, bool explicit_timestamps, const TimeTicks& now, const ThreadTicks& thread_now); // The callbacks will be called up until the point where the flush is // finished, i.e. must be callable until OutputCallback is called with // has_more_events==false. void SetAddTraceEventOverrides( const AddTraceEventOverrideFunction& add_event_override, const OnFlushFunction& on_flush_callback, const UpdateDurationFunction& update_duration_callback); // Called by TRACE_EVENT* macros, don't call this directly. // The name parameter is a category group for example: // TRACE_EVENT0("renderer,webkit", "WebViewImpl::HandleInputEvent") static const unsigned char* GetCategoryGroupEnabled(const char* name); static const char* GetCategoryGroupName( const unsigned char* category_group_enabled); static constexpr const unsigned char* GetBuiltinCategoryEnabled( const char* name) { TraceCategory* builtin_category = CategoryRegistry::GetBuiltinCategoryByName(name); if (builtin_category) return builtin_category->state_ptr(); return nullptr; } // Called by TRACE_EVENT* macros, don't call this directly. // If |copy| is set, |name|, |arg_name1| and |arg_name2| will be deep copied // into the event; see "Memory scoping note" and TRACE_EVENT_COPY_XXX above. bool ShouldAddAfterUpdatingState(char phase, const unsigned char* category_group_enabled, const char* name, uint64_t id, PlatformThreadId thread_id, const TimeTicks timestamp, TraceArguments* args); TraceEventHandle AddTraceEvent(char phase, const unsigned char* category_group_enabled, const char* name, const char* scope, uint64_t id, TraceArguments* args, unsigned int flags); TraceEventHandle AddTraceEventWithBindId( char phase, const unsigned char* category_group_enabled, const char* name, const char* scope, uint64_t id, uint64_t bind_id, TraceArguments* args, unsigned int flags); TraceEventHandle AddTraceEventWithProcessId( char phase, const unsigned char* category_group_enabled, const char* name, const char* scope, uint64_t id, ProcessId process_id, TraceArguments* args, unsigned int flags); TraceEventHandle AddTraceEventWithThreadIdAndTimestamp( char phase, const unsigned char* category_group_enabled, const char* name, const char* scope, uint64_t id, PlatformThreadId thread_id, const TimeTicks& timestamp, TraceArguments* args, unsigned int flags); TraceEventHandle AddTraceEventWithThreadIdAndTimestamp( char phase, const unsigned char* category_group_enabled, const char* name, const char* scope, uint64_t id, uint64_t bind_id, PlatformThreadId thread_id, const TimeTicks& timestamp, TraceArguments* args, unsigned int flags); TraceEventHandle AddTraceEventWithThreadIdAndTimestamps( char phase, const unsigned char* category_group_enabled, const char* name, const char* scope, uint64_t id, uint64_t bind_id, PlatformThreadId thread_id, const TimeTicks& timestamp, const ThreadTicks& thread_timestamp, TraceArguments* args, unsigned int flags); // Adds a metadata event that will be written when the trace log is flushed. void AddMetadataEvent(const unsigned char* category_group_enabled, const char* name, TraceArguments* args, unsigned int flags); void UpdateTraceEventDuration(const unsigned char* category_group_enabled, const char* name, TraceEventHandle handle); void UpdateTraceEventDurationExplicit( const unsigned char* category_group_enabled, const char* name, TraceEventHandle handle, PlatformThreadId thread_id, bool explicit_timestamps, const TimeTicks& now, const ThreadTicks& thread_now); ProcessId process_id() const { return process_id_; } std::unordered_map process_labels() const { AutoLock lock(lock_); return process_labels_; } uint64_t MangleEventId(uint64_t id); // Exposed for unittesting: // Allows clearing up our singleton instance. static void ResetForTesting(); // Allow tests to inspect TraceEvents. TraceEvent* GetEventByHandle(TraceEventHandle handle); void SetProcessID(ProcessId process_id); // Process sort indices, if set, override the order of a process will appear // relative to other processes in the trace viewer. Processes are sorted first // on their sort index, ascending, then by their name, and then tid. void SetProcessSortIndex(int sort_index); // Helper function to set process_name in base::CurrentProcess. void OnSetProcessName(const std::string& process_name); // Processes can have labels in addition to their names. Use labels, for // instance, to list out the web page titles that a process is handling. int GetNewProcessLabelId(); void UpdateProcessLabel(int label_id, const std::string& current_label); void RemoveProcessLabel(int label_id); // Thread sort indices, if set, override the order of a thread will appear // within its process in the trace viewer. Threads are sorted first on their // sort index, ascending, then by their name, and then tid. void SetThreadSortIndex(PlatformThreadId thread_id, int sort_index); #if !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) // Allow setting an offset between the current TimeTicks time and the time // that should be reported. void SetTimeOffset(TimeDelta offset); #endif // !BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) size_t GetObserverCountForTest() const; // Call this method if the current thread may block the message loop to // prevent the thread from using the thread-local buffer because the thread // may not handle the flush request in time causing lost of unflushed events. void SetCurrentThreadBlocksMessageLoop(); #if BUILDFLAG(IS_WIN) // This function is called by the ETW exporting module whenever the ETW // keyword (flags) changes. This keyword indicates which categories should be // exported, so whenever it changes, we adjust accordingly. void UpdateETWCategoryGroupEnabledFlags(); #endif // Replaces |logged_events_| with a new TraceBuffer for testing. void SetTraceBufferForTesting(std::unique_ptr trace_buffer); #if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) struct TrackEventSession { uint32_t internal_instance_index; perfetto::DataSourceConfig config; perfetto::BackendType backend_type = perfetto::kUnspecifiedBackend; }; std::vector GetTrackEventSessions() const; // DEPRECATED. In the presence of multiple simultaneous sessions, this method // returns only the first session's config. When no tracing sessions are // active, returns an empty config for compatibility with legacy code. // TODO(khokhlov): Remove this method and migrate all its uses to // GetTrackEventSessions(). perfetto::DataSourceConfig GetCurrentTrackEventDataSourceConfig() const; void InitializePerfettoIfNeeded(); bool IsPerfettoInitializedByTraceLog() const; void SetEnabledImpl(const TraceConfig& trace_config, const perfetto::TraceConfig& perfetto_config); // perfetto::TrackEventSessionObserver implementation. void OnSetup(const perfetto::DataSourceBase::SetupArgs&) override; void OnStart(const perfetto::DataSourceBase::StartArgs&) override; void OnStop(const perfetto::DataSourceBase::StopArgs&) override; #endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) // Called by the perfetto backend just after incremental state was cleared. void OnIncrementalStateCleared(); private: typedef unsigned int InternalTraceOptions; FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture, TraceBufferRingBufferGetReturnChunk); FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture, TraceBufferRingBufferHalfIteration); FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture, TraceBufferRingBufferFullIteration); FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture, TraceBufferVectorReportFull); FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture, ConvertTraceConfigToInternalOptions); FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture, TraceRecordAsMuchAsPossibleMode); FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture, ConfigTraceBufferLimit); friend class base::NoDestructor; // MemoryDumpProvider implementation. bool OnMemoryDump(const MemoryDumpArgs& args, ProcessMemoryDump* pmd) override; // Enable/disable each category group based on the current mode_, // category_filter_ and event_filters_enabled_. // Enable the category group in the recording mode if category_filter_ matches // the category group, is not null. void UpdateCategoryRegistry(); void UpdateCategoryState(TraceCategory* category); InternalTraceOptions GetInternalOptionsFromTraceConfig( const TraceConfig& config); class OptionalAutoLock; struct RegisteredAsyncObserver; explicit TraceLog(int generation); ~TraceLog() override; void AddMetadataEventsWhileLocked() EXCLUSIVE_LOCKS_REQUIRED(lock_); template void AddMetadataEventWhileLocked(PlatformThreadId thread_id, const char* metadata_name, const char* arg_name, const T& value) EXCLUSIVE_LOCKS_REQUIRED(lock_); InternalTraceOptions trace_options() const { return trace_options_.load(std::memory_order_relaxed); } TraceBuffer* trace_buffer() const { return logged_events_.get(); } TraceBuffer* CreateTraceBuffer(); std::string EventToConsoleMessage(char phase, const TimeTicks& timestamp, TraceEvent* trace_event); TraceEvent* AddEventToThreadSharedChunkWhileLocked(TraceEventHandle* handle, bool check_buffer_is_full) EXCLUSIVE_LOCKS_REQUIRED(lock_); void CheckIfBufferIsFullWhileLocked() EXCLUSIVE_LOCKS_REQUIRED(lock_); void SetDisabledWhileLocked(uint8_t modes) EXCLUSIVE_LOCKS_REQUIRED(lock_); TraceEvent* GetEventByHandleInternal(TraceEventHandle handle, OptionalAutoLock* lock); void FlushInternal(const OutputCallback& cb, bool use_worker_thread, bool discard_events); #if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) void OnTraceData(const char* data, size_t size, bool has_more); #endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) // |generation| is used in the following callbacks to check if the callback // is called for the flush of the current |logged_events_|. void FlushCurrentThread(int generation, bool discard_events); // Usually it runs on a different thread. static void ConvertTraceEventsToTraceFormat( std::unique_ptr logged_events, const TraceLog::OutputCallback& flush_output_callback, const ArgumentFilterPredicate& argument_filter_predicate); void FinishFlush(int generation, bool discard_events); void OnFlushTimeout(int generation, bool discard_events); int generation() const { return generation_.load(std::memory_order_relaxed); } bool CheckGeneration(int generation) const { return generation == this->generation(); } void UseNextTraceBuffer(); TimeTicks OffsetNow() const { // This should be TRACE_TIME_TICKS_NOW but include order makes that hard. return OffsetTimestamp(base::subtle::TimeTicksNowIgnoringOverride()); } TimeTicks OffsetTimestamp(const TimeTicks& timestamp) const { return timestamp - time_offset_; } // Internal representation of trace options since we store the currently used // trace option as an AtomicWord. static const InternalTraceOptions kInternalNone; static const InternalTraceOptions kInternalRecordUntilFull; static const InternalTraceOptions kInternalRecordContinuously; static const InternalTraceOptions kInternalEchoToConsole; static const InternalTraceOptions kInternalRecordAsMuchAsPossible; static const InternalTraceOptions kInternalEnableArgumentFilter; // This lock protects TraceLog member accesses (except for members protected // by thread_info_lock_) from arbitrary threads. mutable Lock lock_; Lock thread_info_lock_; bool enabled_{false}; int num_traces_recorded_{0}; std::unique_ptr logged_events_; std::vector> metadata_events_; // The lock protects observers access. mutable Lock observers_lock_; bool dispatching_to_observers_ = false; std::vector> enabled_state_observers_ GUARDED_BY(observers_lock_); std::map async_observers_ GUARDED_BY(observers_lock_); // Manages ownership of the owned observers. The owned observers will also be // added to |enabled_state_observers_|. std::vector> owned_enabled_state_observer_copy_ GUARDED_BY(observers_lock_); std::vector> incremental_state_observers_ GUARDED_BY(observers_lock_); int next_process_label_id_ GUARDED_BY(lock_) = 0; std::unordered_map process_labels_; int process_sort_index_; std::unordered_map thread_sort_indices_; std::unordered_map thread_names_ GUARDED_BY(thread_info_lock_); // The following two maps are used only when ECHO_TO_CONSOLE. std::unordered_map> thread_event_start_times_ GUARDED_BY(thread_info_lock_); std::unordered_map thread_colors_ GUARDED_BY(thread_info_lock_); TimeTicks buffer_limit_reached_timestamp_; // XORed with TraceID to make it unlikely to collide with other processes. uint64_t process_id_hash_; ProcessId process_id_; TimeDelta time_offset_; std::atomic trace_options_; TraceConfig trace_config_; // Contains task runners for the threads that have had at least one event // added into the local event buffer. std::unordered_map> thread_task_runners_; // For events which can't be added into the thread local buffer, e.g. events // from threads without a message loop. std::unique_ptr thread_shared_chunk_; size_t thread_shared_chunk_index_; // Set when asynchronous Flush is in progress. OutputCallback flush_output_callback_; scoped_refptr flush_task_runner_; ArgumentFilterPredicate argument_filter_predicate_; MetadataFilterPredicate metadata_filter_predicate_; bool record_host_app_package_name_{false}; std::atomic generation_; bool use_worker_thread_; std::atomic add_trace_event_override_{nullptr}; std::atomic on_flush_override_{nullptr}; std::atomic update_duration_override_{nullptr}; #if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) std::unique_ptr tracing_session_; perfetto::TraceConfig perfetto_config_; std::vector track_event_sessions_ GUARDED_BY(track_event_lock_); int active_track_event_sessions_ = 0; mutable Lock track_event_lock_; #if BUILDFLAG(USE_PERFETTO_TRACE_PROCESSOR) std::unique_ptr trace_processor_; std::unique_ptr json_output_writer_; OutputCallback proto_output_callback_; #endif // BUILDFLAG(USE_PERFETTO_TRACE_PROCESSOR) #endif // BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY) #if BUILDFLAG(IS_ANDROID) std::optional atrace_startup_config_; #endif }; } // namespace trace_event } // namespace base #endif // BASE_TRACE_EVENT_TRACE_LOG_H_